mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-03-24 20:40:09 +09:00
Compare commits
89 Commits
76b14eaed7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
729a44e121 | ||
|
|
051ac812db | ||
|
|
01772c4f79 | ||
|
|
9f7c596ca5 | ||
|
|
34bbb48561 | ||
|
|
f060583d6f | ||
|
|
a05b2b76be | ||
|
|
c9f34ac55f | ||
|
|
bc24bba176 | ||
|
|
3154a4ab8d | ||
|
|
9478de8082 | ||
|
|
e927820082 | ||
|
|
67950c620d | ||
|
|
31ea4d1213 | ||
|
|
29c503ed7c | ||
|
|
b1a28f7987 | ||
|
|
616d165f14 | ||
|
|
b9ecb9283e | ||
|
|
c38edb59f2 | ||
|
|
def7a679f8 | ||
|
|
6f671919ad | ||
|
|
45b1d85fb0 | ||
|
|
e7f4cc9580 | ||
|
|
5479b276b2 | ||
|
|
5d7fbcf22a | ||
|
|
123c442a56 | ||
|
|
4dfcdd96b8 | ||
|
|
9d36b9dd81 | ||
|
|
afa21334b4 | ||
|
|
a2ebc7ea9b | ||
|
|
1ab8a35c5d | ||
|
|
8782fcec18 | ||
|
|
8d563f90f3 | ||
|
|
6a5e298cec | ||
|
|
163e1b059b | ||
|
|
e16018491e | ||
|
|
c26e9298e3 | ||
|
|
3c0567cb67 | ||
|
|
00d1903bf6 | ||
|
|
bc2a483aa1 | ||
|
|
5ad8dda6f6 | ||
|
|
d680b8ea8a | ||
|
|
90b07e2c18 | ||
|
|
edbd929fa2 | ||
|
|
38932b74c0 | ||
|
|
60d6179eaa | ||
|
|
0d9b2c68cc | ||
|
|
adc2462329 | ||
|
|
0bff33c790 | ||
|
|
47e9b5536a | ||
|
|
2f1f474e27 | ||
|
|
18d7a3967c | ||
|
|
d45ce10c83 | ||
|
|
a7fd80795e | ||
|
|
f8cba20859 | ||
|
|
f13a8d7ced | ||
|
|
c0963b3417 | ||
|
|
50313883f3 | ||
|
|
7e02580dff | ||
|
|
3232d72812 | ||
|
|
60ad19c2c8 | ||
|
|
6a7f19ec34 | ||
|
|
12ccea8dd8 | ||
|
|
daa80818f8 | ||
|
|
a5eb30dbfd | ||
|
|
1b2a4f760b | ||
|
|
9be83584a5 | ||
|
|
417a095e67 | ||
|
|
a411de5ce8 | ||
|
|
90a5d8f473 | ||
|
|
20856f44c1 | ||
|
|
28d6d10ddc | ||
|
|
b3e13b7f0b | ||
|
|
5654c593df | ||
|
|
b90b7f2451 | ||
|
|
855a0853ad | ||
|
|
f0fdfd4f49 | ||
|
|
dc39902006 | ||
|
|
29dd7874cd | ||
|
|
8a134e03db | ||
|
|
39d931f7e5 | ||
|
|
b4f6d8b800 | ||
|
|
c78d2bb8fb | ||
|
|
5b0cee7c1b | ||
|
|
59ed66b684 | ||
|
|
ce0b616bc6 | ||
|
|
31ceec02fe | ||
|
|
a7cf4bb37b | ||
|
|
3dfaa70fcf |
@@ -784,8 +784,6 @@ coverity:
|
||||
- mkdir obj && cd obj
|
||||
only:
|
||||
- branches@libssh/libssh-mirror
|
||||
- branches@cryptomilk/libssh-mirror
|
||||
- branches@jjelen/libssh-mirror
|
||||
|
||||
# TODO add -DFUZZ_TESTING=ON clang cant find _LLVMFuzzerInitialize on arm64
|
||||
macos-m1:
|
||||
|
||||
101
CHANGELOG
101
CHANGELOG
@@ -1,6 +1,107 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
version 0.12.0 (released 2026-02-10)
|
||||
* Security:
|
||||
* CVE-2025-14821: libssh loads configuration files from the C:\etc directory
|
||||
on Windows
|
||||
* CVE-2026-0964: SCP Protocol Path Traversal in ssh_scp_pull_request()
|
||||
* CVE-2026-0965: Possible Denial of Service when parsing unexpected
|
||||
configuration files
|
||||
* CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input
|
||||
* CVE-2026-0967: Specially crafted patterns could cause DoS
|
||||
* CVE-2026-0968: OOB Read in sftp_parse_longname()
|
||||
* libssh-2026-sftp-extensions: Read buffer overrun when handling SFTP
|
||||
extensions
|
||||
* Deprecations and removals:
|
||||
* Bumped minimal RSA key size to 1024 bits
|
||||
* New functionality:
|
||||
* Add support for hybrid key exchange mechanisms using Quantum Resistant
|
||||
cryptography for all backends. These are now preferred:
|
||||
* sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com
|
||||
* mlkem768nistp256-sha256
|
||||
* mlkem768x25519-sha256
|
||||
* mlkem1024nistp384-sha384 (only OpenSSL 3.5+ and libgcrypt)
|
||||
* New cmake option WITH_HERMETIC_USR
|
||||
* Added support for Ed25519 keys through PKCS#11
|
||||
* Support for host-bound public key authentication
|
||||
(publickey-hostbound-v00@openssh.com)
|
||||
* Use curve25519 implementation from mbedTLS and libgcrypt
|
||||
* New functions for signing arbitrary data (commits) with SSH keys
|
||||
* sshsig_sign()
|
||||
* sshsig_verify()
|
||||
* Support for FIDO/U2F keys (internal implementation using libfido2)
|
||||
* Compatible with OpenSSH: should work out of the box
|
||||
* Extensible with callbacks
|
||||
* Add support for GSSAPI Key Exchange (RFC 4462, RFC 8732)
|
||||
* Add support for new configuration options (client and server):
|
||||
* RequiredRsaSize
|
||||
* AddressFamily (client)
|
||||
* GSSAPIKeyExchange
|
||||
* GSSAPIKexAlgorithms
|
||||
* New option to get list of configured identities (SSH_OPTIONS_NEXT_IDENTITY)
|
||||
* More OpenSSH compatible percent expansion characters
|
||||
* Add new server auth_kbdint_function() callback
|
||||
* New PKI Context structure for key operations
|
||||
* Stability and compatibility improvements of ProxyJump
|
||||
* SFTP
|
||||
* Prevent failures when SFTP status message does not contain error message
|
||||
* Fix possible timeouts while waiting for SFTP messages
|
||||
* Support for users-groups-by-id@openssh.com extension in client
|
||||
* Support for SSH_FXF_TRUNC in server
|
||||
|
||||
version 0.11.4 (released 2026-02-10)
|
||||
* Security:
|
||||
* CVE-2025-14821: libssh loads configuration files from the C:\etc directory
|
||||
on Windows
|
||||
* CVE-2026-0964: SCP Protocol Path Traversal in ssh_scp_pull_request()
|
||||
* CVE-2026-0965: Possible Denial of Service when parsing unexpected
|
||||
configuration files
|
||||
* CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input
|
||||
* CVE-2026-0967: Specially crafted patterns could cause DoS
|
||||
* CVE-2026-0968: OOB Read in sftp_parse_longname()
|
||||
* libssh-2026-sftp-extensions: Read buffer overrun when handling SFTP
|
||||
extensions
|
||||
* Stability and compatibility improvements of ProxyJump
|
||||
|
||||
version 0.11.3 (released 2025-09-09)
|
||||
* Security:
|
||||
* CVE-2025-8114: Fix NULL pointer dereference after allocation failure
|
||||
* CVE-2025-8277: Fix memory leak of ephemeral key pair during repeated wrong KEX
|
||||
* Potential UAF when send() fails during key exchange
|
||||
* Fix possible timeout during KEX if client sends authentication too early (#311)
|
||||
* Cleanup OpenSSL PKCS#11 provider when loaded
|
||||
* Zeroize buffers containing private key blobs during export
|
||||
|
||||
version 0.11.2 (released 2025-06-24)
|
||||
* Security:
|
||||
* CVE-2025-4877 - Write beyond bounds in binary to base64 conversion
|
||||
* CVE-2025-4878 - Use of uninitialized variable in privatekey_from_file()
|
||||
* CVE-2025-5318 - Likely read beyond bounds in sftp server handle management
|
||||
* CVE-2025-5351 - Double free in functions exporting keys
|
||||
* CVE-2025-5372 - ssh_kdf() returns a success code on certain failures
|
||||
* CVE-2025-5449 - Likely read beyond bounds in sftp server message decoding
|
||||
* CVE-2025-5987 - Invalid return code for chacha20 poly1305 with OpenSSL
|
||||
* Compatibility
|
||||
* Fixed compatibility with CPM.cmake
|
||||
* Compatibility with OpenSSH 10.0
|
||||
* Tests compatibility with new Dropbear releases
|
||||
* Removed p11-kit remoting from the pkcs11 testsuite
|
||||
* Bugfixes
|
||||
* Implement missing packet filter for DH GEX
|
||||
* Properly process the SSH2_MSG_DEBUG message
|
||||
* Allow escaping quotes in quoted arguments to ssh configuration
|
||||
* Do not fail with unknown match keywords in ssh configuration
|
||||
* Process packets before selecting signature algorithm during authentication
|
||||
* Do not fail hard when the SFTP status message is not sent by noncompliant
|
||||
servers
|
||||
|
||||
version 0.11.1 (released 2024-08-30)
|
||||
* Fixed default TTY modes that are set when stdin is not connected to tty (#270)
|
||||
* Fixed zlib cleanup procedure, which could crash on i386
|
||||
* Various test fixes improving their stability
|
||||
* Fixed cygwin build
|
||||
|
||||
version 0.11.0 (released 2024-07-31)
|
||||
* Deprecations and Removals:
|
||||
* Dropped support for DSA
|
||||
|
||||
@@ -9,7 +9,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
|
||||
include(DefineCMakeDefaults)
|
||||
include(DefineCompilerFlags)
|
||||
|
||||
project(libssh VERSION 0.11.00 LANGUAGES C)
|
||||
project(libssh VERSION 0.12.00 LANGUAGES C)
|
||||
|
||||
# global needed variable
|
||||
set(APPLICATION_NAME ${PROJECT_NAME})
|
||||
@@ -21,7 +21,7 @@ set(APPLICATION_NAME ${PROJECT_NAME})
|
||||
# Increment AGE. Set REVISION to 0
|
||||
# If the source code was changed, but there were no interface changes:
|
||||
# Increment REVISION.
|
||||
set(LIBRARY_VERSION "4.10.0")
|
||||
set(LIBRARY_VERSION "4.11.0")
|
||||
set(LIBRARY_SOVERSION "4")
|
||||
|
||||
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
|
||||
|
||||
@@ -12,21 +12,36 @@ option(WITH_PCAP "Compile with Pcap generation support" ON)
|
||||
option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
option(WITH_PKCS11_URI "Build with PKCS#11 URI support" OFF)
|
||||
option(WITH_PKCS11_PROVIDER "Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
|
||||
option(WITH_PKCS11_PROVIDER
|
||||
"Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
|
||||
option(WITH_FIDO2 "Build with FIDO2/U2F support" OFF)
|
||||
option(UNIT_TESTING "Build with unit tests" OFF)
|
||||
option(CLIENT_TESTING "Build with client tests; requires openssh" OFF)
|
||||
option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" OFF)
|
||||
option(GSSAPI_TESTING "Build with GSSAPI tests; requires krb5-server,krb5-libs and krb5-workstation" OFF)
|
||||
option(WITH_BENCHMARKS "Build benchmarks tools; enables unit testing and client tests" OFF)
|
||||
option(SERVER_TESTING "Build with server tests; requires openssh and dropbear"
|
||||
OFF)
|
||||
option(
|
||||
GSSAPI_TESTING
|
||||
"Build with GSSAPI tests; requires krb5-server,krb5-libs and krb5-workstation"
|
||||
OFF)
|
||||
option(WITH_BENCHMARKS
|
||||
"Build benchmarks tools; enables unit testing and client tests" OFF)
|
||||
option(WITH_EXAMPLES "Build examples" ON)
|
||||
option(WITH_NACL "Build with libnacl (curve25519)" ON)
|
||||
option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON)
|
||||
option(WITH_ABI_BREAK "Allow ABI break" OFF)
|
||||
option(WITH_GEX "Enable DH Group exchange mechanisms" ON)
|
||||
option(WITH_INSECURE_NONE "Enable insecure none cipher and MAC algorithms (not suitable for production!)" OFF)
|
||||
option(WITH_EXEC "Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)." ON)
|
||||
option(FUZZ_TESTING "Build with fuzzer for the server and client (automatically enables none cipher!)" OFF)
|
||||
option(
|
||||
WITH_INSECURE_NONE
|
||||
"Enable insecure none cipher and MAC algorithms (not suitable for production!)"
|
||||
OFF)
|
||||
option(
|
||||
WITH_EXEC
|
||||
"Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)."
|
||||
ON)
|
||||
option(
|
||||
FUZZ_TESTING
|
||||
"Build with fuzzer for the server and client (automatically enables none cipher!)"
|
||||
OFF)
|
||||
option(PICKY_DEVELOPER "Build with picky developer flags" OFF)
|
||||
option(WITH_HERMETIC_USR "Build with support for hermetic /usr/" OFF)
|
||||
|
||||
@@ -37,39 +52,58 @@ else (WITH_ZLIB)
|
||||
endif (WITH_ZLIB)
|
||||
|
||||
if (WITH_BENCHMARKS)
|
||||
set(UNIT_TESTING ON)
|
||||
set(CLIENT_TESTING ON)
|
||||
endif()
|
||||
set(UNIT_TESTING ON)
|
||||
set(CLIENT_TESTING ON)
|
||||
endif ()
|
||||
|
||||
if (UNIT_TESTING OR CLIENT_TESTING OR SERVER_TESTING OR GSSAPI_TESTING)
|
||||
set(BUILD_STATIC_LIB ON)
|
||||
endif()
|
||||
if (UNIT_TESTING
|
||||
OR CLIENT_TESTING
|
||||
OR SERVER_TESTING
|
||||
OR GSSAPI_TESTING)
|
||||
set(BUILD_STATIC_LIB ON)
|
||||
endif ()
|
||||
|
||||
if (WITH_NACL)
|
||||
set(WITH_NACL ON)
|
||||
set(WITH_NACL ON)
|
||||
endif (WITH_NACL)
|
||||
|
||||
if (WITH_ABI_BREAK)
|
||||
set(WITH_SYMBOL_VERSIONING ON)
|
||||
set(WITH_SYMBOL_VERSIONING ON)
|
||||
endif (WITH_ABI_BREAK)
|
||||
|
||||
set(GLOBAL_CONF_DIR "/etc/ssh")
|
||||
if (WIN32)
|
||||
# Use PROGRAMDATA on Windows
|
||||
if (DEFINED ENV{PROGRAMDATA})
|
||||
set(GLOBAL_CONF_DIR "$ENV{PROGRAMDATA}/ssh")
|
||||
else ()
|
||||
set(GLOBAL_CONF_DIR "C:/ProgramData/ssh")
|
||||
endif ()
|
||||
if (WITH_HERMETIC_USR)
|
||||
set(USR_GLOBAL_CONF_DIR "/usr${GLOBAL_CONF_DIR}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT GLOBAL_BIND_CONFIG)
|
||||
set(GLOBAL_BIND_CONFIG "/etc/ssh/libssh_server_config")
|
||||
set(GLOBAL_BIND_CONFIG "${GLOBAL_CONF_DIR}/libssh_server_config")
|
||||
|
||||
if (WITH_HERMETIC_USR)
|
||||
set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
|
||||
endif ()
|
||||
endif (NOT GLOBAL_BIND_CONFIG)
|
||||
|
||||
if (NOT GLOBAL_CLIENT_CONFIG)
|
||||
set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config")
|
||||
set(GLOBAL_CLIENT_CONFIG "${GLOBAL_CONF_DIR}/ssh_config")
|
||||
|
||||
if (WITH_HERMETIC_USR)
|
||||
set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
|
||||
endif ()
|
||||
endif (NOT GLOBAL_CLIENT_CONFIG)
|
||||
|
||||
if (WITH_HERMETIC_USR)
|
||||
set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
|
||||
set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
|
||||
endif (WITH_HERMETIC_USR)
|
||||
|
||||
if (FUZZ_TESTING)
|
||||
set(WITH_INSECURE_NONE ON)
|
||||
set(WITH_INSECURE_NONE ON)
|
||||
endif (FUZZ_TESTING)
|
||||
|
||||
if (WIN32)
|
||||
set(WITH_EXEC 0)
|
||||
endif(WIN32)
|
||||
endif (WIN32)
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#cmakedefine BINARYDIR "${BINARYDIR}"
|
||||
#cmakedefine SOURCEDIR "${SOURCEDIR}"
|
||||
|
||||
/* Global configuration directory */
|
||||
#cmakedefine USR_GLOBAL_CONF_DIR "${USR_GLOBAL_CONF_DIR}"
|
||||
#cmakedefine GLOBAL_CONF_DIR "${GLOBAL_CONF_DIR}"
|
||||
|
||||
/* Global bind configuration file path */
|
||||
#cmakedefine USR_GLOBAL_BIND_CONFIG "${USR_GLOBAL_BIND_CONFIG}"
|
||||
#cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}"
|
||||
|
||||
@@ -108,7 +108,6 @@ if (DOXYGEN_FOUND)
|
||||
packet_struct,
|
||||
pem_get_password_struct,
|
||||
ssh_tokens_st,
|
||||
sftp_attributes_struct,
|
||||
sftp_client_message_struct,
|
||||
sftp_dir_struct,
|
||||
sftp_ext_struct,
|
||||
|
||||
@@ -49,4 +49,4 @@ ALL_FUNC=$(echo "$FUNC_LINES" | sed -e "s/$F_CUT_BEFORE//g" -e "s/$F_CUT_AFTER//
|
||||
ALL_FUNC=$(echo "$ALL_FUNC" | sort - | uniq | wc -l)
|
||||
|
||||
# percentage of the documented functions
|
||||
awk "BEGIN {printf \"Documentation coverage is %.2f%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"
|
||||
awk "BEGIN {printf \"Documentation coverage is %.2f%%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"
|
||||
|
||||
@@ -190,7 +190,6 @@ int verify_knownhost(ssh_session session)
|
||||
ssh_key srv_pubkey = NULL;
|
||||
size_t hlen;
|
||||
char buf[10];
|
||||
char *hexa = NULL;
|
||||
char *p = NULL;
|
||||
int cmp;
|
||||
int rc;
|
||||
@@ -201,7 +200,7 @@ int verify_knownhost(ssh_session session)
|
||||
}
|
||||
|
||||
rc = ssh_get_publickey_hash(srv_pubkey,
|
||||
SSH_PUBLICKEY_HASH_SHA1,
|
||||
SSH_PUBLICKEY_HASH_SHA256,
|
||||
&hash,
|
||||
&hlen);
|
||||
ssh_key_free(srv_pubkey);
|
||||
@@ -217,7 +216,7 @@ int verify_knownhost(ssh_session session)
|
||||
break;
|
||||
case SSH_KNOWN_HOSTS_CHANGED:
|
||||
fprintf(stderr, "Host key for server changed: it is now:\n");
|
||||
ssh_print_hexa("Public key hash", hash, hlen);
|
||||
ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
|
||||
fprintf(stderr, "For security reasons, connection will be stopped\n");
|
||||
ssh_clean_pubkey_hash(&hash);
|
||||
|
||||
@@ -238,10 +237,9 @@ int verify_knownhost(ssh_session session)
|
||||
/* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */
|
||||
|
||||
case SSH_KNOWN_HOSTS_UNKNOWN:
|
||||
hexa = ssh_get_hexa(hash, hlen);
|
||||
fprintf(stderr,"The server is unknown. Do you trust the host key?\n");
|
||||
fprintf(stderr, "Public key hash: %s\n", hexa);
|
||||
ssh_string_free_char(hexa);
|
||||
fprintf(stderr, "Public key hash: ");
|
||||
ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
|
||||
ssh_clean_pubkey_hash(&hash);
|
||||
p = fgets(buf, sizeof(buf), stdin);
|
||||
if (p == NULL) {
|
||||
|
||||
@@ -24,7 +24,7 @@ int main(void)
|
||||
int rv;
|
||||
|
||||
/* Generate a new ED25519 private key file */
|
||||
rv = ssh_pki_generate(SSH_KEYTYPE_ED25519, 0, &key);
|
||||
rv = ssh_pki_generate_key(SSH_KEYTYPE_ED25519, NULL, &key);
|
||||
if (rv != SSH_OK) {
|
||||
fprintf(stderr, "Failed to generate private key");
|
||||
return -1;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
struct arguments_st {
|
||||
enum ssh_keytypes_e type;
|
||||
unsigned long bits;
|
||||
int bits;
|
||||
char *file;
|
||||
char *passphrase;
|
||||
char *format;
|
||||
@@ -321,8 +321,9 @@ list_fingerprint(char *file)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
ssh_pki_ctx ctx = NULL;
|
||||
ssh_key key = NULL;
|
||||
int rc = 0;
|
||||
int ret = EXIT_FAILURE, rc, fd;
|
||||
char overwrite[1024] = "";
|
||||
|
||||
char *pubkey_file = NULL;
|
||||
@@ -361,15 +362,15 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
rc = open(arguments.file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (rc < 0) {
|
||||
fd = open(arguments.file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST) {
|
||||
printf("File \"%s\" exists. Overwrite it? (y|n) ", arguments.file);
|
||||
rc = scanf("%1023s", overwrite);
|
||||
if (rc > 0 && tolower(overwrite[0]) == 'y') {
|
||||
rc = open(arguments.file, O_WRONLY);
|
||||
if (rc > 0) {
|
||||
close(rc);
|
||||
fd = open(arguments.file, O_WRONLY);
|
||||
if (fd > 0) {
|
||||
close(fd);
|
||||
errno = 0;
|
||||
rc = chmod(arguments.file, S_IRUSR | S_IWUSR);
|
||||
if (rc != 0) {
|
||||
@@ -391,13 +392,30 @@ int main(int argc, char *argv[])
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
close(rc);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Create a new PKI Context if needed -- for other types using NULL is ok */
|
||||
if (arguments.type == SSH_KEYTYPE_RSA && arguments.bits != 0) {
|
||||
ctx = ssh_pki_ctx_new();
|
||||
if (ctx == NULL) {
|
||||
fprintf(stderr, "Error: Failed to allocate PKI context\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = ssh_pki_ctx_options_set(ctx,
|
||||
SSH_PKI_OPTION_RSA_KEY_SIZE,
|
||||
&arguments.bits);
|
||||
if (rc != SSH_OK) {
|
||||
fprintf(stderr, "Error: Failed to set RSA bit size\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate a new private key */
|
||||
rc = ssh_pki_generate(arguments.type, arguments.bits, &key);
|
||||
rc = ssh_pki_generate_key(arguments.type, ctx, &key);
|
||||
if (rc != SSH_OK) {
|
||||
fprintf(stderr, "Error: Failed to generate keys");
|
||||
fprintf(stderr, "Error: Failed to generate keys\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -451,24 +469,23 @@ int main(int argc, char *argv[])
|
||||
|
||||
pubkey_file = (char *)malloc(strlen(arguments.file) + 5);
|
||||
if (pubkey_file == NULL) {
|
||||
rc = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
sprintf(pubkey_file, "%s.pub", arguments.file);
|
||||
|
||||
errno = 0;
|
||||
rc = open(pubkey_file,
|
||||
fd = open(pubkey_file,
|
||||
O_CREAT | O_EXCL | O_WRONLY,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (rc < 0) {
|
||||
if (fd < 0) {
|
||||
if (errno == EEXIST) {
|
||||
printf("File \"%s\" exists. Overwrite it? (y|n) ", pubkey_file);
|
||||
rc = scanf("%1023s", overwrite);
|
||||
if (rc > 0 && tolower(overwrite[0]) == 'y') {
|
||||
rc = open(pubkey_file, O_WRONLY);
|
||||
if (rc > 0) {
|
||||
close(rc);
|
||||
fd = open(pubkey_file, O_WRONLY);
|
||||
if (fd > 0) {
|
||||
close(fd);
|
||||
errno = 0;
|
||||
rc = chmod(pubkey_file,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
@@ -491,7 +508,7 @@ int main(int argc, char *argv[])
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
close(rc);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Write the public key */
|
||||
@@ -501,14 +518,12 @@ int main(int argc, char *argv[])
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
if (key != NULL) {
|
||||
ssh_key_free(key);
|
||||
}
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
if (arguments.file != NULL) {
|
||||
free(arguments.file);
|
||||
}
|
||||
end:
|
||||
ssh_pki_ctx_free(ctx);
|
||||
ssh_key_free(key);
|
||||
free(arguments.file);
|
||||
|
||||
if (arguments.passphrase != NULL) {
|
||||
#ifdef HAVE_EXPLICIT_BZERO
|
||||
@@ -519,8 +534,6 @@ end:
|
||||
free(arguments.passphrase);
|
||||
}
|
||||
|
||||
if (pubkey_file != NULL) {
|
||||
free(pubkey_file);
|
||||
}
|
||||
return rc;
|
||||
free(pubkey_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* This is a sample implementation of a libssh based SSH server */
|
||||
/* This is a sample implementation of a libssh based SFTP server */
|
||||
/*
|
||||
Copyright 2014 Audrius Butkevicius
|
||||
|
||||
@@ -9,6 +9,28 @@ domain. This does not apply to the rest of the library though, but it is
|
||||
allowed to cut-and-paste working code from this file to any license of
|
||||
program.
|
||||
The goal is to show the API in action.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
|
||||
This is not a production-ready SFTP server implementation. While it demonstrates
|
||||
how an SFTP server can be implemented on the SFTP layer and integrated into
|
||||
existing SSH server, it lacks many steps int the authentication and
|
||||
session establishment!
|
||||
|
||||
It allows to log in any user with hardcoded credentials below or with public
|
||||
key provided from authorized keys file.
|
||||
|
||||
The resulting SFTP session keeps running under original user who runs the
|
||||
example server and therefore the SFTP session has access to all files that are
|
||||
accessible to the user running the server.
|
||||
|
||||
Real-world servers should at very least switch the user to unprivileged one
|
||||
after authentication using setuid(). If some more restrictions are needed,
|
||||
generally limiting what files should and should not be accessible, it is
|
||||
recommended to use chroot() as handling symlinks can be tricky in the SFTP
|
||||
callbacks.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -10,6 +10,23 @@ allowed to cut-and-paste working code from this file to any license of
|
||||
program.
|
||||
The goal is to show the API in action. It's not a reference on how terminal
|
||||
clients must be made or how a client should react.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
|
||||
This is not a production-ready SSH server implementation. While it demonstrates
|
||||
how an SSH server can be implemented, it lacks many steps during
|
||||
the authentication and session establishment!
|
||||
|
||||
It allows to log in any user with hardcoded credentials below or with public
|
||||
key provided from authorized keys file.
|
||||
|
||||
The resulting session keeps running under original user who runs the example
|
||||
server and therefore it retains the same permissions.
|
||||
|
||||
Real-world servers should at very least switch the user to unprivileged one
|
||||
after authentication using setuid().
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -337,6 +337,7 @@ static void batch_shell(ssh_session session)
|
||||
static int client(ssh_session session)
|
||||
{
|
||||
int auth = 0;
|
||||
int authenticated = 0;
|
||||
char *banner = NULL;
|
||||
int state;
|
||||
|
||||
@@ -369,16 +370,28 @@ static int client(ssh_session session)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssh_userauth_none(session, NULL);
|
||||
banner = ssh_get_issue_banner(session);
|
||||
if (banner) {
|
||||
printf("%s\n", banner);
|
||||
free(banner);
|
||||
}
|
||||
auth = authenticate_console(session);
|
||||
if (auth != SSH_AUTH_SUCCESS) {
|
||||
auth = ssh_userauth_none(session, NULL);
|
||||
if (auth == SSH_AUTH_SUCCESS) {
|
||||
authenticated = 1;
|
||||
} else if (auth == SSH_AUTH_ERROR) {
|
||||
fprintf(stderr,
|
||||
"Authentication error during none auth: %s\n",
|
||||
ssh_get_error(session));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!authenticated) {
|
||||
auth = authenticate_console(session);
|
||||
if (auth != SSH_AUTH_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmds[0] == NULL) {
|
||||
shell(session);
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,23 @@ domain. This does not apply to the rest of the library though, but it is
|
||||
allowed to cut-and-paste working code from this file to any license of
|
||||
program.
|
||||
The goal is to show the API in action.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
|
||||
This is not a production-ready SSH server implementation. While it demonstrates
|
||||
how an SSH server can be implemented, it lacks many steps during
|
||||
the authentication and session establishment!
|
||||
|
||||
It allows to log in any user with hardcoded credentials below or with public
|
||||
key provided from authorized keys file.
|
||||
|
||||
The resulting session keeps running under original user who runs the example
|
||||
server and therefore it retains the same permissions.
|
||||
|
||||
Real-world servers should at very least switch the user to unprivileged one
|
||||
after authentication using setuid().
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
@@ -107,6 +124,14 @@ static struct argp_option options[] = {
|
||||
.doc = "Set the authorized keys file.",
|
||||
.group = 0
|
||||
},
|
||||
{
|
||||
.name = "option",
|
||||
.key = 'o',
|
||||
.arg = "OPTION",
|
||||
.flags = 0,
|
||||
.doc = "Set server configuration option [-o OptionName=Value]",
|
||||
.group = 0
|
||||
},
|
||||
{
|
||||
.name = "user",
|
||||
.key = 'u',
|
||||
@@ -158,6 +183,9 @@ parse_opt(int key, char *arg, struct argp_state *state)
|
||||
case 'a':
|
||||
strncpy(authorizedkeys, arg, DEF_STR_SIZE - 1);
|
||||
break;
|
||||
case 'o':
|
||||
ssh_bind_config_parse_string(sshbind, arg);
|
||||
break;
|
||||
case 'u':
|
||||
strncpy(username, arg, sizeof(username) - 1);
|
||||
break;
|
||||
@@ -194,7 +222,7 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
|
||||
{
|
||||
int key;
|
||||
|
||||
while((key = getopt(argc, argv, "a:e:k:p:P:r:u:v")) != -1) {
|
||||
while((key = getopt(argc, argv, "a:e:k:o:p:P:r:u:v")) != -1) {
|
||||
if (key == 'p') {
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, optarg);
|
||||
} else if (key == 'k') {
|
||||
@@ -205,6 +233,8 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
|
||||
} else if (key == 'a') {
|
||||
strncpy(authorizedkeys, optarg, DEF_STR_SIZE-1);
|
||||
} else if (key == 'o') {
|
||||
ssh_bind_config_parse_string(sshbind, optarg);
|
||||
} else if (key == 'u') {
|
||||
strncpy(username, optarg, sizeof(username) - 1);
|
||||
} else if (key == 'P') {
|
||||
@@ -222,6 +252,7 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
|
||||
"libssh %s -- a Secure Shell protocol implementation\n"
|
||||
"\n"
|
||||
" -a, --authorizedkeys=FILE Set the authorized keys file.\n"
|
||||
" -o, --option=OPTION Set server configuration option (e.g., -o OptionName=Value).\n"
|
||||
" -e, --ecdsakey=FILE Set the ecdsa key (deprecated alias for 'k').\n"
|
||||
" -k, --hostkey=FILE Set a host key. Can be used multiple times.\n"
|
||||
" Implies no default keys.\n"
|
||||
|
||||
@@ -74,6 +74,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SSH agent connection context.
|
||||
*
|
||||
* This structure represents a connection to an SSH authentication agent.
|
||||
* It is used by libssh to communicate with the agent for operations such
|
||||
* as listing available identities and signing data during authentication.
|
||||
*/
|
||||
struct ssh_agent_struct {
|
||||
struct ssh_socket_struct *sock;
|
||||
ssh_buffer ident;
|
||||
@@ -82,13 +89,24 @@ struct ssh_agent_struct {
|
||||
};
|
||||
|
||||
/* agent.c */
|
||||
|
||||
/**
|
||||
* @brief Create a new ssh agent structure.
|
||||
*
|
||||
* @return An allocated ssh agent structure or NULL on error.
|
||||
* Creates and initializes an SSH agent context bound to the given
|
||||
* SSH session. The agent connection relies on the session configuration
|
||||
* (e.g. agent forwarding).
|
||||
*
|
||||
* @param session The SSH session the agent will be associated with.
|
||||
*
|
||||
* @return An allocated ssh agent structure on success, or NULL on error.
|
||||
*
|
||||
* @note This function does not start or manage an external agent
|
||||
* process; it only connects to an already running agent.
|
||||
*/
|
||||
struct ssh_agent_struct *ssh_agent_new(struct ssh_session_struct *session);
|
||||
|
||||
|
||||
void ssh_agent_close(struct ssh_agent_struct *agent);
|
||||
|
||||
/**
|
||||
@@ -101,23 +119,75 @@ void ssh_agent_free(struct ssh_agent_struct *agent);
|
||||
/**
|
||||
* @brief Check if the ssh agent is running.
|
||||
*
|
||||
* @param session The ssh session to check for the agent.
|
||||
* @param session The SSH session to check for agent availability.
|
||||
*
|
||||
* @return 1 if it is running, 0 if not.
|
||||
* @return 1 if an agent is available, 0 otherwise.
|
||||
*/
|
||||
int ssh_agent_is_running(struct ssh_session_struct *session);
|
||||
|
||||
uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session);
|
||||
|
||||
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
|
||||
char **comment);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the first identity provided by the SSH agent.
|
||||
*
|
||||
* @param session The SSH session associated with the agent.
|
||||
* @param comment Optional pointer to receive the key comment.
|
||||
*
|
||||
* @return A public key on success, or NULL if no identities are available.
|
||||
*
|
||||
* @note The returned key is owned by the caller and must be freed
|
||||
* using ssh_key_free().
|
||||
*/
|
||||
ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
|
||||
char **comment);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the next identity provided by the SSH agent.
|
||||
*
|
||||
* @param session The SSH session associated with the agent.
|
||||
* @param comment Optional pointer to receive the key comment.
|
||||
*
|
||||
* @return A public key on success, or NULL if no further identities exist.
|
||||
*
|
||||
* @note The returned key is owned by the caller and must be freed
|
||||
* using ssh_key_free().
|
||||
*/
|
||||
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
|
||||
char **comment);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Request the SSH agent to sign data using a public key.
|
||||
*
|
||||
* Asks the SSH agent to generate a signature over the provided data
|
||||
* using the specified public key.
|
||||
*
|
||||
* @param session The SSH session associated with the agent.
|
||||
* @param pubkey The public key identifying the signing identity.
|
||||
* @param data The data to be signed.
|
||||
*
|
||||
* @return A newly allocated ssh_string containing the signature on
|
||||
* success, or NULL on failure.
|
||||
*
|
||||
* @note This operation requires that the agent possesses the
|
||||
* corresponding private key and may prompt the user for
|
||||
* confirmation depending on agent configuration.
|
||||
*/
|
||||
ssh_string ssh_agent_sign_data(ssh_session session,
|
||||
const ssh_key pubkey,
|
||||
struct ssh_buffer_struct *data);
|
||||
/**
|
||||
* @brief Remove an identity from the SSH agent.
|
||||
*
|
||||
* @param session The SSH session.
|
||||
* @param key The public key to remove.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
|
||||
*/
|
||||
int ssh_agent_remove_identity(ssh_session session,
|
||||
const ssh_key key);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -57,25 +57,16 @@ enum ssh_bind_config_opcode_e {
|
||||
BIND_CFG_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
/* @brief Parse configuration file and set the options to the given ssh_bind
|
||||
/**
|
||||
* @brief Parse configuration file and set the options to the given ssh_bind
|
||||
*
|
||||
* @params[in] sshbind The ssh_bind context to be configured
|
||||
* @params[in] filename The path to the configuration file
|
||||
* @param[in] sshbind The ssh_bind context to be configured
|
||||
* @param[in] filename The path to the configuration file
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
int ssh_bind_config_parse_file(ssh_bind sshbind, const char *filename);
|
||||
|
||||
/* @brief Parse configuration string and set the options to the given bind session
|
||||
*
|
||||
* @params[in] bind The ssh bind session
|
||||
* @params[in] input Null terminated string containing the configuration
|
||||
*
|
||||
* @returns SSH_OK on successful parsing the configuration string,
|
||||
* SSH_ERROR on error
|
||||
*/
|
||||
int ssh_bind_config_parse_string(ssh_bind bind, const char *input);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#ifndef _BLF_H_
|
||||
#define _BLF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//#include "includes.h"
|
||||
|
||||
#if !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H)
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#ifndef _BYTEARRAY_H
|
||||
#define _BYTEARRAY_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define _DATA_BYTE_CONST(data, pos) \
|
||||
((uint8_t)(((const uint8_t *)(data))[(pos)]))
|
||||
|
||||
|
||||
@@ -522,7 +522,7 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
|
||||
* verifies that the callback pointer exists
|
||||
* @param p callback pointer
|
||||
* @param c callback name
|
||||
* @returns nonzero if callback can be called
|
||||
* @return nonzero if callback can be called
|
||||
*/
|
||||
#define ssh_callbacks_exists(p,c) (\
|
||||
(p != NULL) && ( (char *)&((p)-> c) < (char *)(p) + (p)->size ) && \
|
||||
@@ -538,12 +538,9 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
|
||||
* automatically passed through.
|
||||
*
|
||||
* @param list list of callbacks
|
||||
*
|
||||
* @param cbtype type of the callback
|
||||
*
|
||||
* @param c callback name
|
||||
*
|
||||
* @param va_args parameters to be passed
|
||||
* @param ... Parameters to be passed to the callback.
|
||||
*/
|
||||
#define ssh_callbacks_execute_list(list, cbtype, c, ...) \
|
||||
do { \
|
||||
@@ -585,9 +582,18 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
|
||||
_cb = ssh_iterator_value(_cb_type, _cb_i); \
|
||||
if (ssh_callbacks_exists(_cb, _cb_name))
|
||||
|
||||
/** @internal
|
||||
* @brief Execute the current callback in an ssh_callbacks_iterate() loop.
|
||||
*
|
||||
* @param _cb_name The name of the callback field to invoke.
|
||||
* @param ... Parameters to be passed to the callback.
|
||||
*/
|
||||
#define ssh_callbacks_iterate_exec(_cb_name, ...) \
|
||||
_cb->_cb_name(__VA_ARGS__, _cb->userdata)
|
||||
|
||||
/** @internal
|
||||
* @brief End an ssh_callbacks_iterate() loop.
|
||||
*/
|
||||
#define ssh_callbacks_iterate_end() \
|
||||
} \
|
||||
} while(0)
|
||||
@@ -1060,8 +1066,18 @@ LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Callback for thread mutex operations (init, destroy, lock, unlock).
|
||||
*
|
||||
* @param lock Pointer to the mutex lock.
|
||||
*
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
typedef int (*ssh_thread_callback) (void **lock);
|
||||
|
||||
/** @brief Callback to retrieve the current thread identifier.
|
||||
*
|
||||
* @return The unique identifier of the calling thread.
|
||||
*/
|
||||
typedef unsigned long (*ssh_thread_id_callback) (void);
|
||||
struct ssh_threads_callbacks_struct {
|
||||
const char *type;
|
||||
@@ -1172,10 +1188,20 @@ typedef int (*ssh_jump_verify_knownhost_callback)(ssh_session session,
|
||||
typedef int (*ssh_jump_authenticate_callback)(ssh_session session,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Callback collection for managing an SSH proxyjump connection.
|
||||
*
|
||||
* Set these callbacks to control knownhost verification and authentication
|
||||
* on the jump host before the final destination is reached.
|
||||
*/
|
||||
struct ssh_jump_callbacks_struct {
|
||||
/** Userdata passed to each callback. */
|
||||
void *userdata;
|
||||
/** Called before connecting to the jump host. */
|
||||
ssh_jump_before_connection_callback before_connection;
|
||||
/** Called to verify the jump host's identity. */
|
||||
ssh_jump_verify_knownhost_callback verify_knownhost;
|
||||
/** Called to authenticate on the jump host. */
|
||||
ssh_jump_authenticate_callback authenticate;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ Public domain.
|
||||
#ifndef CHACHA_H
|
||||
#define CHACHA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct chacha_ctx {
|
||||
uint32_t input[16];
|
||||
};
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#ifndef CHACHA20_POLY1305_H
|
||||
#define CHACHA20_POLY1305_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CHACHA20_BLOCKSIZE 64
|
||||
#define CHACHA20_KEYLEN 32
|
||||
|
||||
|
||||
@@ -54,6 +54,11 @@ int ssh_config_get_yesno(char **str, int notfound);
|
||||
* be stored or NULL if we do not care about the result.
|
||||
* @param[in] ignore_port Set to true if we should not attempt to parse
|
||||
* port number.
|
||||
* @param[in] strict Set to true to validate hostname against RFC1035
|
||||
* (for resolving to a real host).
|
||||
* Set to false to only reject shell metacharacters
|
||||
* (allowing config aliases with non-RFC1035 chars
|
||||
* like underscores, resolved later via Hostname).
|
||||
*
|
||||
* @returns SSH_OK if the provided string is in format of SSH URI,
|
||||
* SSH_ERROR on failure
|
||||
@@ -62,7 +67,8 @@ int ssh_config_parse_uri(const char *tok,
|
||||
char **username,
|
||||
char **hostname,
|
||||
char **port,
|
||||
bool ignore_port);
|
||||
bool ignore_port,
|
||||
bool strict);
|
||||
|
||||
/**
|
||||
* @brief: Parse the ProxyJump configuration line and if parsing,
|
||||
|
||||
@@ -25,14 +25,19 @@
|
||||
#ifndef _CRYPTO_H_
|
||||
#define _CRYPTO_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef HAVE_LIBGCRYPT
|
||||
#include <gcrypt.h>
|
||||
#elif defined(HAVE_LIBMBEDCRYPTO)
|
||||
#include <mbedtls/gcm.h>
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_ECDH_H
|
||||
#include <openssl/ecdh.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/wrapper.h"
|
||||
|
||||
#ifdef cbc_encrypt
|
||||
@@ -42,9 +47,6 @@
|
||||
#undef cbc_decrypt
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENSSL_ECDH_H
|
||||
#include <openssl/ecdh.h>
|
||||
#endif
|
||||
#include "libssh/curve25519.h"
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/ecdh.h"
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
#ifndef SRC_DH_GEX_H_
|
||||
#define SRC_DH_GEX_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#ifndef SSH_KNOWNHOSTS_H_
|
||||
#define SSH_KNOWNHOSTS_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -24,8 +24,9 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_LIBGCRYPT
|
||||
|
||||
#include <gcrypt.h>
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
typedef gcry_md_hd_t SHACTX;
|
||||
typedef gcry_md_hd_t SHA256CTX;
|
||||
typedef gcry_md_hd_t SHA384CTX;
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_LIBMBEDCRYPTO
|
||||
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/pk.h>
|
||||
@@ -36,6 +35,8 @@
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
#include <mbedtls/platform.h>
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
typedef mbedtls_md_context_t *SHACTX;
|
||||
typedef mbedtls_md_context_t *SHA256CTX;
|
||||
typedef mbedtls_md_context_t *SHA384CTX;
|
||||
|
||||
@@ -432,6 +432,7 @@ enum ssh_options_e {
|
||||
SSH_OPTIONS_ADDRESS_FAMILY,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
|
||||
SSH_OPTIONS_NEXT_IDENTITY,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -67,38 +67,60 @@ class Channel;
|
||||
*/
|
||||
#ifndef SSH_NO_CPP_EXCEPTIONS
|
||||
|
||||
/** @brief This class describes a SSH Exception object. This object can be thrown
|
||||
* by several SSH functions that interact with the network, and may fail because of
|
||||
* socket, protocol or memory errors.
|
||||
/** @brief This class describes a SSH Exception object. This object can be
|
||||
* thrown by several SSH functions that interact with the network, and
|
||||
* may fail because of socket, protocol or memory errors.
|
||||
*/
|
||||
class SshException{
|
||||
public:
|
||||
SshException(ssh_session csession){
|
||||
code=ssh_get_error_code(csession);
|
||||
description=std::string(ssh_get_error(csession));
|
||||
}
|
||||
SshException(const SshException &e){
|
||||
code=e.code;
|
||||
description=e.description;
|
||||
}
|
||||
/** @brief returns the Error code
|
||||
* @returns SSH_FATAL Fatal error happened (not recoverable)
|
||||
* @returns SSH_REQUEST_DENIED Request was denied by remote host
|
||||
* @see ssh_get_error_code
|
||||
*/
|
||||
int getCode(){
|
||||
return code;
|
||||
}
|
||||
/** @brief returns the error message of the last exception
|
||||
* @returns pointer to a c string containing the description of error
|
||||
* @see ssh_get_error
|
||||
*/
|
||||
std::string getError(){
|
||||
return description;
|
||||
}
|
||||
private:
|
||||
int code;
|
||||
std::string description;
|
||||
class SshException {
|
||||
public:
|
||||
/** @brief Construct an exception from a libssh session error state.
|
||||
* Captures the current error code and error string associated with
|
||||
* the given session.
|
||||
*
|
||||
* @param[in] csession libssh session handle used to query error details.
|
||||
*
|
||||
* @see ssh_get_error_code
|
||||
* @see ssh_get_error
|
||||
*/
|
||||
SshException(ssh_session csession)
|
||||
{
|
||||
code = ssh_get_error_code(csession);
|
||||
description = std::string(ssh_get_error(csession));
|
||||
}
|
||||
/** @brief Copy-construct an exception.
|
||||
*
|
||||
* @param[in] e Source exception.
|
||||
*/
|
||||
SshException(const SshException &e)
|
||||
{
|
||||
code = e.code;
|
||||
description = e.description;
|
||||
}
|
||||
/** @brief returns the Error code
|
||||
*
|
||||
* @returns `SSH_FATAL` Fatal error happened (not recoverable)
|
||||
* @returns `SSH_REQUEST_DENIED` Request was denied by remote host
|
||||
*
|
||||
* @see ssh_get_error_code
|
||||
*/
|
||||
int getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
/** @brief returns the error message of the last exception
|
||||
*
|
||||
* @returns pointer to a c string containing the description of error
|
||||
*
|
||||
* @see ssh_get_error
|
||||
*/
|
||||
std::string getError()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
private:
|
||||
int code;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
/** @internal
|
||||
@@ -134,9 +156,12 @@ public:
|
||||
c_session=NULL;
|
||||
}
|
||||
/** @brief sets an SSH session options
|
||||
* @param type Type of option
|
||||
* @param option cstring containing the value of option
|
||||
*
|
||||
* @param[in] type Type of option
|
||||
* @param[in] option cstring containing the value of option
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @see ssh_options_set
|
||||
*/
|
||||
void_throwable setOption(enum ssh_options_e type, const char *option){
|
||||
@@ -144,9 +169,12 @@ public:
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief sets an SSH session options
|
||||
* @param type Type of option
|
||||
* @param option long integer containing the value of option
|
||||
*
|
||||
* @param[in] type Type of option
|
||||
* @param[in] option long integer containing the value of option
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @see ssh_options_set
|
||||
*/
|
||||
void_throwable setOption(enum ssh_options_e type, long int option){
|
||||
@@ -154,9 +182,12 @@ public:
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief sets an SSH session options
|
||||
* @param type Type of option
|
||||
* @param option void pointer containing the value of option
|
||||
*
|
||||
* @param[in] type Type of option
|
||||
* @param[in] option void pointer containing the value of option
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @see ssh_options_set
|
||||
*/
|
||||
void_throwable setOption(enum ssh_options_e type, void *option){
|
||||
@@ -164,7 +195,9 @@ public:
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief connects to the remote host
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @see ssh_connect
|
||||
*/
|
||||
void_throwable connect(){
|
||||
@@ -173,8 +206,11 @@ public:
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief Authenticates automatically using public key
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
|
||||
*
|
||||
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
|
||||
*
|
||||
* @see ssh_userauth_autopubkey
|
||||
*/
|
||||
int userauthPublickeyAuto(void){
|
||||
@@ -184,9 +220,13 @@ public:
|
||||
}
|
||||
/** @brief Authenticates using the "none" method. Prefer using autopubkey if
|
||||
* possible.
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
|
||||
*
|
||||
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
|
||||
*
|
||||
* @see ssh_userauth_none
|
||||
*
|
||||
* @see Session::userauthAutoPubkey
|
||||
*/
|
||||
int userauthNone(){
|
||||
@@ -198,16 +238,16 @@ public:
|
||||
/**
|
||||
* @brief Authenticate through the "keyboard-interactive" method.
|
||||
*
|
||||
* @param[in] username The username to authenticate. You can specify NULL if
|
||||
* ssh_option_set_username() has been used. You cannot
|
||||
* try two different logins in a row.
|
||||
*
|
||||
* @param[in] username The username to authenticate. You can specify NULL
|
||||
* If ssh_option_set_username()has been used. You cannot
|
||||
* try two different logins in a row.
|
||||
* @param[in] submethods Undocumented. Set it to NULL.
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED,
|
||||
* SSH_AUTH_ERROR, SSH_AUTH_INFO, SSH_AUTH_AGAIN
|
||||
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`,
|
||||
* `SSH_AUTH_DENIED`, `SSH_AUTH_ERROR`, `SSH_AUTH_INFO`,
|
||||
* `SSH_AUTH_AGAIN`
|
||||
*
|
||||
* @see ssh_userauth_kbdint
|
||||
*/
|
||||
@@ -218,6 +258,7 @@ public:
|
||||
}
|
||||
|
||||
/** @brief Get the number of prompts (questions) the server has given.
|
||||
*
|
||||
* @returns The number of prompts.
|
||||
* @see ssh_userauth_kbdint_getnprompts
|
||||
*/
|
||||
@@ -228,17 +269,16 @@ public:
|
||||
/**
|
||||
* @brief Set the answer for a question from a message block.
|
||||
*
|
||||
* @param[in] index The index number of the prompt.
|
||||
* @param[in] answer The answer to give to the server. The answer MUST be
|
||||
* encoded UTF-8. It is up to the server how to interpret
|
||||
* the value and validate it. However, if you read the
|
||||
* answer in some other encoding, you MUST convert it to
|
||||
* UTF-8.
|
||||
* @param[in] index The index number of the prompt.
|
||||
* @param[in] answer The answer to give to the server. The answer MUST be
|
||||
* encoded UTF-8.It is up to the server how to interpret
|
||||
* the value and validate it. However, if you read the
|
||||
* answer in some other encoding, you MUST convert it to
|
||||
* UTF-8.
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @returns 0 on success, < 0 on error
|
||||
*
|
||||
* @see ssh_userauth_kbdint_setanswer
|
||||
*/
|
||||
int userauthKbdintSetAnswer(unsigned int index, const char *answer)
|
||||
@@ -248,12 +288,13 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @brief Authenticates using the password method.
|
||||
*
|
||||
* @param[in] password password to use for authentication
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
|
||||
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
|
||||
*
|
||||
* @see ssh_userauth_password
|
||||
*/
|
||||
int userauthPassword(const char *password){
|
||||
@@ -262,10 +303,14 @@ public:
|
||||
return ret;
|
||||
}
|
||||
/** @brief Try to authenticate using the publickey method.
|
||||
*
|
||||
* @param[in] pubkey public key to use for authentication
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @returns SSH_AUTH_SUCCESS if the pubkey is accepted,
|
||||
* @returns SSH_AUTH_DENIED if the pubkey is denied
|
||||
*
|
||||
* @returns `SSH_AUTH_SUCCESS` if the pubkey is accepted,
|
||||
* @returns `SSH_AUTH_DENIED` if the pubkey is denied
|
||||
*
|
||||
* @see ssh_userauth_try_pubkey
|
||||
*/
|
||||
int userauthTryPublickey(ssh_key pubkey){
|
||||
@@ -274,9 +319,12 @@ public:
|
||||
return ret;
|
||||
}
|
||||
/** @brief Authenticates using the publickey method.
|
||||
*
|
||||
* @param[in] privkey private key to use for authentication
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
|
||||
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
|
||||
*
|
||||
* @see ssh_userauth_pubkey
|
||||
*/
|
||||
int userauthPublickey(ssh_key privkey){
|
||||
@@ -286,7 +334,9 @@ public:
|
||||
}
|
||||
|
||||
/** @brief Returns the available authentication methods from the server
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @returns Bitfield of available methods.
|
||||
* @see ssh_userauth_list
|
||||
*/
|
||||
@@ -302,8 +352,9 @@ public:
|
||||
ssh_disconnect(c_session);
|
||||
}
|
||||
/** @brief Returns the disconnect message from the server, if any
|
||||
* @returns pointer to the message, or NULL. Do not attempt to free
|
||||
* the pointer.
|
||||
*
|
||||
* @returns pointer to the message, or NULL. Do not attempt to free the
|
||||
* pointer.
|
||||
*/
|
||||
const char *getDisconnectMessage(){
|
||||
const char *msg=ssh_get_disconnect_message(c_session);
|
||||
@@ -312,25 +363,30 @@ public:
|
||||
/** @internal
|
||||
* @brief gets error message
|
||||
*/
|
||||
const char *getError(){
|
||||
return ssh_get_error(c_session);
|
||||
const char *getError()
|
||||
{
|
||||
return ssh_get_error(c_session);
|
||||
}
|
||||
/** @internal
|
||||
* @brief returns error code
|
||||
*/
|
||||
int getErrorCode(){
|
||||
return ssh_get_error_code(c_session);
|
||||
int getErrorCode()
|
||||
{
|
||||
return ssh_get_error_code(c_session);
|
||||
}
|
||||
/** @brief returns the file descriptor used for the communication
|
||||
*
|
||||
* @returns the file descriptor
|
||||
*
|
||||
* @warning if a proxycommand is used, this function will only return
|
||||
* one of the two file descriptors being used
|
||||
* one of the two file descriptors being used.
|
||||
* @see ssh_get_fd
|
||||
*/
|
||||
socket_t getSocket(){
|
||||
return ssh_get_fd(c_session);
|
||||
}
|
||||
/** @brief gets the Issue banner from the ssh server
|
||||
*
|
||||
* @returns the issue banner. This is generally a MOTD from server
|
||||
* @see ssh_get_issue_banner
|
||||
*/
|
||||
@@ -344,6 +400,7 @@ public:
|
||||
return ret;
|
||||
}
|
||||
/** @brief returns the OpenSSH version (server) if possible
|
||||
*
|
||||
* @returns openssh version code
|
||||
* @see ssh_get_openssh_version
|
||||
*/
|
||||
@@ -351,6 +408,7 @@ public:
|
||||
return ssh_get_openssh_version(c_session);
|
||||
}
|
||||
/** @brief returns the version of the SSH protocol being used
|
||||
*
|
||||
* @returns the SSH protocol version
|
||||
* @see ssh_get_version
|
||||
*/
|
||||
@@ -358,9 +416,10 @@ public:
|
||||
return ssh_get_version(c_session);
|
||||
}
|
||||
/** @brief verifies that the server is known
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @returns Integer value depending on the knowledge of the
|
||||
* server key
|
||||
*
|
||||
* @returns Integer value depending on the knowledge of the server key
|
||||
* @see ssh_session_update_known_hosts
|
||||
*/
|
||||
int isServerKnown(){
|
||||
@@ -377,6 +436,7 @@ public:
|
||||
}
|
||||
|
||||
/** @brief copies options from a session to another
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @see ssh_options_copy
|
||||
*/
|
||||
@@ -385,8 +445,11 @@ public:
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief parses a configuration file for options
|
||||
*
|
||||
* @throws SshException on error
|
||||
*
|
||||
* @param[in] file configuration file name
|
||||
*
|
||||
* @see ssh_options_parse_config
|
||||
*/
|
||||
void_throwable optionsParseConfig(const char *file){
|
||||
@@ -399,8 +462,8 @@ public:
|
||||
void silentDisconnect(){
|
||||
ssh_silent_disconnect(c_session);
|
||||
}
|
||||
/** @brief Writes the known host file with current
|
||||
* host key
|
||||
/** @brief Writes the known host file with current host key
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @see ssh_write_knownhost
|
||||
*/
|
||||
@@ -411,11 +474,15 @@ public:
|
||||
}
|
||||
|
||||
/** @brief accept an incoming forward connection
|
||||
*
|
||||
* @param[in] timeout_ms timeout for waiting, in ms
|
||||
*
|
||||
* @returns new Channel pointer on the forward connection
|
||||
* @returns NULL in case of error
|
||||
*
|
||||
* @warning you have to delete this pointer after use
|
||||
* @see ssh_channel_forward_accept
|
||||
*
|
||||
* @see Session::listenForward
|
||||
*/
|
||||
inline Channel *acceptForward(int timeout_ms);
|
||||
@@ -439,6 +506,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
/** @internal
|
||||
* @brief Underlying libssh session handle.
|
||||
*/
|
||||
ssh_session c_session;
|
||||
|
||||
private:
|
||||
@@ -447,8 +517,7 @@ private:
|
||||
Session& operator=(const Session &);
|
||||
};
|
||||
|
||||
/** @brief the ssh::Channel class describes the state of an SSH
|
||||
* channel.
|
||||
/** @brief the ssh::Channel class describes the state of an SSH channel.
|
||||
* @see ssh_channel
|
||||
*/
|
||||
class Channel {
|
||||
@@ -464,11 +533,15 @@ public:
|
||||
}
|
||||
|
||||
/** @brief accept an incoming X11 connection
|
||||
*
|
||||
* @param[in] timeout_ms timeout for waiting, in ms
|
||||
*
|
||||
* @returns new Channel pointer on the X11 connection
|
||||
* @returns NULL in case of error
|
||||
*
|
||||
* @warning you have to delete this pointer after use
|
||||
* @see ssh_channel_accept_x11
|
||||
*
|
||||
* @see Channel::requestX11
|
||||
*/
|
||||
Channel *acceptX11(int timeout_ms){
|
||||
@@ -478,8 +551,10 @@ public:
|
||||
return newchan;
|
||||
}
|
||||
/** @brief change the size of a pseudoterminal
|
||||
*
|
||||
* @param[in] cols number of columns
|
||||
* @param[in] rows number of rows
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @see ssh_channel_change_pty_size
|
||||
*/
|
||||
@@ -490,6 +565,7 @@ public:
|
||||
}
|
||||
|
||||
/** @brief closes a channel
|
||||
*
|
||||
* @throws SshException on error
|
||||
* @see ssh_channel_close
|
||||
*/
|
||||
@@ -536,6 +612,21 @@ public:
|
||||
bool isOpen(){
|
||||
return ssh_channel_is_open(channel) != 0;
|
||||
}
|
||||
/** @brief Open a TCP forward channel.
|
||||
*
|
||||
* @param[in] remotehost Remote host to connect to.
|
||||
* @param[in] remoteport Remote port to connect to.
|
||||
* @param[in] sourcehost Source address to report (can be NULL depending on
|
||||
* server policy).
|
||||
* @param[in] localport Local port to report (0 lets the server pick if
|
||||
* applicable).
|
||||
*
|
||||
* @returns `SSH_OK` (0) on success.
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*
|
||||
* @throws SshException on error (exception-enabled builds).
|
||||
* @see ssh_channel_open_forward
|
||||
*/
|
||||
int openForward(const char *remotehost, int remoteport,
|
||||
const char *sourcehost, int localport=0){
|
||||
int err=ssh_channel_open_forward(channel,remotehost,remoteport,
|
||||
@@ -549,20 +640,55 @@ public:
|
||||
ssh_throw(err);
|
||||
return_throwable;
|
||||
}
|
||||
int poll(bool is_stderr=false){
|
||||
int err=ssh_channel_poll(channel,is_stderr);
|
||||
ssh_throw(err);
|
||||
return err;
|
||||
/** @brief Poll the channel for available data.
|
||||
*
|
||||
* @param[in] is_stderr If true, poll stderr stream; otherwise stdout.
|
||||
*
|
||||
* @returns Number of bytes available to read (>= 0).
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*
|
||||
* @throws SshException on error (exception-enabled builds).
|
||||
* @see ssh_channel_poll
|
||||
*/
|
||||
int poll(bool is_stderr = false)
|
||||
{
|
||||
int err = ssh_channel_poll(channel, is_stderr);
|
||||
ssh_throw(err);
|
||||
return err;
|
||||
}
|
||||
int read(void *dest, size_t count){
|
||||
int err;
|
||||
/* handle int overflow */
|
||||
if(count > 0x7fffffff)
|
||||
count = 0x7fffffff;
|
||||
err=ssh_channel_read_timeout(channel,dest,count,false,-1);
|
||||
ssh_throw(err);
|
||||
return err;
|
||||
/** @brief Read data from the channel (blocking).
|
||||
*
|
||||
* @param[out] dest Destination buffer.
|
||||
* @param[in] count Maximum number of bytes to read.
|
||||
*
|
||||
* @returns Number of bytes read (>= 0). A return of 0 indicates EOF/no data.
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*
|
||||
* @throws SshException on error (exception-enabled builds).
|
||||
* @see ssh_channel_read_timeout
|
||||
*/
|
||||
int read(void *dest, size_t count)
|
||||
{
|
||||
int err;
|
||||
if (count > 0x7fffffff)
|
||||
count = 0x7fffffff;
|
||||
err = ssh_channel_read_timeout(channel, dest, count, false, -1);
|
||||
ssh_throw(err);
|
||||
return err;
|
||||
}
|
||||
/** @brief Read data from the channel with a timeout.
|
||||
*
|
||||
* @param[out] dest Destination buffer.
|
||||
* @param[in] count Maximum number of bytes to read.
|
||||
* @param[in] timeout Timeout in milliseconds. A negative value means
|
||||
* infinite timeout.
|
||||
*
|
||||
* @returns Number of bytes read (>= 0). A return value of 0 indicates EOF.
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*
|
||||
* @throws SshException on error (exception-enabled builds).
|
||||
* @see ssh_channel_read_timeout
|
||||
*/
|
||||
int read(void *dest, size_t count, int timeout){
|
||||
int err;
|
||||
/* handle int overflow */
|
||||
@@ -572,6 +698,22 @@ public:
|
||||
ssh_throw(err);
|
||||
return err;
|
||||
}
|
||||
/** @brief Read data from the channel with optional stderr selection and
|
||||
* timeout.
|
||||
*
|
||||
* @param[out] dest Destination buffer.
|
||||
* @param[in] count Maximum number of bytes to read.
|
||||
* @param[in] is_stderr If true, read from the stderr stream; otherwise
|
||||
* read from stdout.
|
||||
* @param[in] timeout Timeout in milliseconds. A negative value means
|
||||
* infinite timeout.
|
||||
*
|
||||
* @returns Number of bytes read (>= 0). A return value of 0 indicates EOF.
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*
|
||||
* @throws SshException on error (exception-enabled builds).
|
||||
* @see ssh_channel_read_timeout
|
||||
*/
|
||||
int read(void *dest, size_t count, bool is_stderr=false, int timeout=-1){
|
||||
int err;
|
||||
/* handle int overflow */
|
||||
@@ -581,6 +723,19 @@ public:
|
||||
ssh_throw(err);
|
||||
return err;
|
||||
}
|
||||
/** @brief Read data from the channel without blocking.
|
||||
*
|
||||
* @param[out] dest Destination buffer.
|
||||
* @param[in] count Maximum number of bytes to read.
|
||||
* @param[in] is_stderr If true, read from the stderr stream; otherwise read
|
||||
* from stdout.
|
||||
*
|
||||
* @returns Number of bytes read (>= 0). A return of 0 may indicate no data.
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*
|
||||
* @throws SshException on error (exception-enabled builds).
|
||||
* @see ssh_channel_read_nonblocking
|
||||
*/
|
||||
int readNonblocking(void *dest, size_t count, bool is_stderr=false){
|
||||
int err;
|
||||
/* handle int overflow */
|
||||
@@ -629,6 +784,18 @@ public:
|
||||
ssh_throw(err);
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief Request X11 forwarding for this channel.
|
||||
*
|
||||
* @param[in] single_connection If true, allow only a single X11 connection
|
||||
* for this channel; further X11 connections are
|
||||
* refused after the first is accepted.
|
||||
* @param[in] protocol X11 authentication protocol.
|
||||
* @param[in] cookie X11 authentication cookie.
|
||||
* @param[in] screen_number X11 screen number.
|
||||
*
|
||||
* @returns `SSH_OK` on success.
|
||||
* @returns `SSH_ERROR` on error (no-exception builds).
|
||||
*/
|
||||
int requestX11(bool single_connection,
|
||||
const char *protocol, const char *cookie, int screen_number){
|
||||
int err=ssh_channel_request_x11(channel,single_connection,
|
||||
@@ -641,11 +808,16 @@ public:
|
||||
ssh_throw(err);
|
||||
return_throwable;
|
||||
}
|
||||
/** @brief Writes on a channel
|
||||
* @param data data to write.
|
||||
* @param len number of bytes to write.
|
||||
* @param is_stderr write should be done on the stderr channel (server only)
|
||||
/**
|
||||
* @brief Writes on a channel
|
||||
*
|
||||
* @param[in] data data to write.
|
||||
* @param[in] len number of bytes to write.
|
||||
* @param[in] is_stderr write should be done on the stderr channel (server
|
||||
* only)
|
||||
*
|
||||
* @returns number of bytes written
|
||||
*
|
||||
* @throws SshException in case of error
|
||||
* @see ssh_channel_write
|
||||
* @see ssh_channel_write_stderr
|
||||
@@ -670,7 +842,13 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
/** @internal
|
||||
* @brief Parent session owning this channel.
|
||||
*/
|
||||
Session *session;
|
||||
/** @internal
|
||||
* @brief Underlying libssh channel handle.
|
||||
*/
|
||||
ssh_channel channel;
|
||||
|
||||
private:
|
||||
@@ -683,7 +861,6 @@ private:
|
||||
Channel &operator=(const Channel &);
|
||||
};
|
||||
|
||||
|
||||
inline Channel *Session::acceptForward(int timeout_ms){
|
||||
ssh_channel forward =
|
||||
ssh_channel_open_forward_port(c_session, timeout_ms, NULL, NULL, NULL);
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libssh/callbacks.h"
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
struct ssh_auth_request {
|
||||
char *username;
|
||||
int method;
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#ifndef MISC_H_
|
||||
#define MISC_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "config.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef _MSC_VER
|
||||
# ifndef _SSIZE_T_DEFINED
|
||||
# undef ssize_t
|
||||
@@ -31,11 +32,13 @@
|
||||
# define _SSIZE_T_DEFINED
|
||||
# endif /* _SSIZE_T_DEFINED */
|
||||
# endif /* _MSC_VER */
|
||||
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#endif /* _WIN32 */
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -43,8 +46,9 @@ extern "C" {
|
||||
|
||||
/* in misc.c */
|
||||
/* gets the user home dir. */
|
||||
char *ssh_get_user_home_dir(void);
|
||||
char *ssh_get_user_home_dir(ssh_session session);
|
||||
char *ssh_get_local_username(void);
|
||||
char *ssh_get_local_hostname(void);
|
||||
int ssh_file_readaccess_ok(const char *file);
|
||||
int ssh_dir_writeable(const char *path);
|
||||
|
||||
@@ -57,8 +61,8 @@ int ssh_is_ipaddr(const char *str);
|
||||
/* list processing */
|
||||
|
||||
struct ssh_list {
|
||||
struct ssh_iterator *root;
|
||||
struct ssh_iterator *end;
|
||||
struct ssh_iterator *root;
|
||||
struct ssh_iterator *end;
|
||||
};
|
||||
|
||||
struct ssh_iterator {
|
||||
@@ -66,9 +70,15 @@ struct ssh_iterator {
|
||||
const void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Holds connection details for an SSH proxyjump host.
|
||||
*/
|
||||
struct ssh_jump_info_struct {
|
||||
/** Hostname or IP address of the jump host. */
|
||||
char *hostname;
|
||||
/** Username to authenticate with on the jump host. */
|
||||
char *username;
|
||||
/** Port number of the jump host. */
|
||||
int port;
|
||||
};
|
||||
|
||||
@@ -136,6 +146,8 @@ int ssh_check_username_syntax(const char *username);
|
||||
void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list);
|
||||
bool ssh_libssh_proxy_jumps(void);
|
||||
|
||||
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,13 @@
|
||||
#ifndef _OPTIONS_H
|
||||
#define _OPTIONS_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#ifndef PACKET_H_
|
||||
#define PACKET_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libssh/callbacks.h"
|
||||
#include "libssh/wrapper.h"
|
||||
|
||||
struct ssh_socket_struct;
|
||||
|
||||
@@ -130,14 +130,15 @@ extern "C" {
|
||||
/* SSH Key Functions */
|
||||
void ssh_key_clean (ssh_key key);
|
||||
|
||||
const char *
|
||||
ssh_key_get_signature_algorithm(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name);
|
||||
const char *ssh_key_get_signature_algorithm(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type);
|
||||
enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
enum ssh_digest_e ssh_key_hash_from_name(const char *name);
|
||||
|
||||
int ssh_key_type_and_hash_from_signature_name(const char *name,
|
||||
enum ssh_keytypes_e *type,
|
||||
enum ssh_digest_e *hash_type);
|
||||
|
||||
#define is_ecdsa_key_type(t) \
|
||||
((t) >= SSH_KEYTYPE_ECDSA_P256 && (t) <= SSH_KEYTYPE_ECDSA_P521)
|
||||
|
||||
@@ -24,8 +24,12 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_POLL
|
||||
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
#ifdef HAVE_POLL
|
||||
typedef struct pollfd ssh_pollfd_t;
|
||||
|
||||
#else /* HAVE_POLL */
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#ifndef POLY1305_H
|
||||
#define POLY1305_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libssh/chacha20-poly1305-common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -508,6 +508,9 @@ char *ssh_strerror(int err_num, char *buf, size_t buflen);
|
||||
#define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
|
||||
int encode_current_tty_opts(unsigned char *buf, size_t buflen);
|
||||
|
||||
/** The default maximum file size for a configuration file */
|
||||
#define SSH_MAX_CONFIG_FILE_SIZE 16 * 1024 * 1024
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#ifndef SC25519_H
|
||||
#define SC25519_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define sc25519 crypto_sign_ed25519_ref_sc25519
|
||||
#define shortsc25519 crypto_sign_ed25519_ref_shortsc25519
|
||||
#define sc25519_from32bytes crypto_sign_ed25519_ref_sc25519_from32bytes
|
||||
|
||||
@@ -21,6 +21,13 @@
|
||||
#ifndef _SCP_H
|
||||
#define _SCP_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
enum ssh_scp_states {
|
||||
SSH_SCP_NEW, //Data structure just created
|
||||
SSH_SCP_WRITE_INITED, //Gave our intention to write
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Options for configuring an SSH server bind session.
|
||||
*
|
||||
* Used with ssh_bind_options_set() to configure server-side options.
|
||||
*/
|
||||
enum ssh_bind_options_e {
|
||||
SSH_BIND_OPTIONS_BINDADDR,
|
||||
SSH_BIND_OPTIONS_BINDPORT,
|
||||
@@ -102,12 +107,31 @@ LIBSSH_API int ssh_bind_options_set(ssh_bind sshbind,
|
||||
LIBSSH_API int ssh_bind_options_parse_config(ssh_bind sshbind,
|
||||
const char *filename);
|
||||
|
||||
LIBSSH_API int ssh_bind_config_parse_string(ssh_bind bind, const char *input);
|
||||
|
||||
/**
|
||||
* @brief Start listening to the socket.
|
||||
*
|
||||
* @param ssh_bind_o The ssh server bind to use.
|
||||
*
|
||||
* @return 0 on success, < 0 on error.
|
||||
*
|
||||
* @warning This function implicitly calls ssh_bind_options_parse_config()
|
||||
* to process system-wide and user configuration files unless
|
||||
* configuration processing was already performed explicitly
|
||||
* by the caller.\n
|
||||
* As a result, any options previously set (e.g., manually via
|
||||
* ssh_bind_options_set() or ssh_bind_config_parse_string()) may be
|
||||
* overridden by values from the configuration files.\n
|
||||
* To guarantee that explicitly set options take precedence,
|
||||
* callers of this function should either:
|
||||
* - call ssh_bind_options_parse_config() themselves before
|
||||
* setting options, or
|
||||
* - disable automatic config processing via
|
||||
* SSH_BIND_OPTIONS_PROCESS_CONFIG (set to false).
|
||||
*
|
||||
* @see ssh_bind_options_parse_config()
|
||||
* @see ssh_bind_options_set()
|
||||
*/
|
||||
LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o);
|
||||
|
||||
|
||||
@@ -246,13 +246,16 @@ struct ssh_session_struct {
|
||||
struct {
|
||||
struct ssh_list *identity;
|
||||
struct ssh_list *identity_non_exp;
|
||||
struct ssh_iterator *identity_it;
|
||||
struct ssh_list *certificate;
|
||||
struct ssh_list *certificate_non_exp;
|
||||
struct ssh_list *proxy_jumps;
|
||||
struct ssh_list *proxy_jumps_user_cb;
|
||||
char *proxy_jumps_str;
|
||||
char *username;
|
||||
char *host;
|
||||
char *bindaddr; /* bind the client to an ip addr */
|
||||
char *homedir;
|
||||
char *sshdir;
|
||||
char *knownhosts;
|
||||
char *global_knownhosts;
|
||||
@@ -283,6 +286,9 @@ struct ssh_session_struct {
|
||||
int control_master;
|
||||
char *control_path;
|
||||
int address_family;
|
||||
char *originalhost; /* user-supplied host for config matching */
|
||||
bool config_hostname_only; /* config hostname path: update host only,
|
||||
not originalhost */
|
||||
} opts;
|
||||
|
||||
/* server options */
|
||||
|
||||
@@ -64,6 +64,7 @@ extern "C" {
|
||||
#endif /* _MSC_VER */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/** @brief The SFTP protocol version implemented by this library. */
|
||||
#define LIBSFTP_VERSION 3
|
||||
|
||||
typedef struct sftp_attributes_struct* sftp_attributes;
|
||||
@@ -90,10 +91,76 @@ typedef struct sftp_request_queue_struct* sftp_request_queue;
|
||||
* @see sftp_free
|
||||
*/
|
||||
typedef struct sftp_session_struct* sftp_session;
|
||||
|
||||
/**
|
||||
* @brief Handle for an SFTP status message received from the server.
|
||||
*
|
||||
* This type represents a status response returned by the SFTP server in
|
||||
* reply to a client request. It carries a numeric status code and an optional
|
||||
* human-readable error message. This type is used internally by libssh and
|
||||
* is not part of the public API.
|
||||
*
|
||||
* @see sftp_status_message_struct
|
||||
*/
|
||||
typedef struct sftp_status_message_struct* sftp_status_message;
|
||||
|
||||
/**
|
||||
* @brief Handle for SFTP file system statistics.
|
||||
*
|
||||
* This type represents file system statistics as reported by the SFTP server,
|
||||
* analogous to the POSIX @c statvfs structure. It is obtained via
|
||||
* sftp_statvfs() or sftp_fstatvfs() and must be freed with
|
||||
* sftp_statvfs_free().
|
||||
*
|
||||
* @see sftp_statvfs_struct
|
||||
* @see sftp_statvfs
|
||||
* @see sftp_fstatvfs
|
||||
* @see sftp_statvfs_free
|
||||
*/
|
||||
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
|
||||
|
||||
/**
|
||||
* @brief Handle for SFTP server limits information.
|
||||
*
|
||||
* This type represents the server-reported SFTP limits such as the maximum
|
||||
* packet, read, and write lengths and the maximum number of open handles.
|
||||
* It is obtained via sftp_limits() and must be freed with sftp_limits_free().
|
||||
*
|
||||
* @see sftp_limits_struct
|
||||
* @see sftp_limits
|
||||
* @see sftp_limits_free
|
||||
*/
|
||||
typedef struct sftp_limits_struct* sftp_limits_t;
|
||||
|
||||
/**
|
||||
* @brief Handle for an asynchronous SFTP I/O operation.
|
||||
*
|
||||
* This type represents an in-flight asynchronous SFTP read or write request.
|
||||
* It is allocated by sftp_aio_begin_read() or sftp_aio_begin_write() and
|
||||
* consumed by the corresponding sftp_aio_wait_read() or sftp_aio_wait_write()
|
||||
* call. If the wait call is not reached, the handle must be freed explicitly
|
||||
* with sftp_aio_free() to avoid memory leaks.
|
||||
*
|
||||
* @see sftp_aio_begin_read
|
||||
* @see sftp_aio_wait_read
|
||||
* @see sftp_aio_begin_write
|
||||
* @see sftp_aio_wait_write
|
||||
* @see sftp_aio_free
|
||||
*/
|
||||
typedef struct sftp_aio_struct* sftp_aio;
|
||||
|
||||
/**
|
||||
* @brief Handle for an SFTP name-to-id mapping.
|
||||
*
|
||||
* This type stores a mapping between numeric user or group IDs and their
|
||||
* corresponding names. It is allocated via sftp_name_id_map_new(), populated
|
||||
* by sftp_get_users_groups_by_id(), and freed with sftp_name_id_map_free().
|
||||
*
|
||||
* @see sftp_name_id_map_struct
|
||||
* @see sftp_name_id_map_new
|
||||
* @see sftp_get_users_groups_by_id
|
||||
* @see sftp_name_id_map_free
|
||||
*/
|
||||
typedef struct sftp_name_id_map_struct *sftp_name_id_map;
|
||||
|
||||
struct sftp_session_struct {
|
||||
@@ -177,28 +244,113 @@ struct sftp_status_message_struct {
|
||||
char *langmsg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SFTP file attributes structure.
|
||||
*
|
||||
* This type represents file attributes. It is used both for
|
||||
* sending and receiving file attributes from the server.
|
||||
*
|
||||
* `flags` determines which of the struct fields are present.
|
||||
*
|
||||
* @see sftp_attributes_free()
|
||||
*/
|
||||
struct sftp_attributes_struct {
|
||||
/** File name */
|
||||
char *name;
|
||||
char *longname; /* ls -l output on openssh, not reliable else */
|
||||
|
||||
/** Extended name i.e output of `ls -l` (requires SFTP v3 with OpenSSH) */
|
||||
char *longname;
|
||||
|
||||
/** Determines which of the struct fields are present */
|
||||
uint32_t flags;
|
||||
|
||||
/** File type */
|
||||
uint8_t type;
|
||||
|
||||
/** File size (requires flag `SSH_FILEXFER_ATTR_SIZE`) */
|
||||
uint64_t size;
|
||||
|
||||
/** User ID (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_UIDGID`) */
|
||||
uint32_t uid;
|
||||
|
||||
/** Group ID (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_UIDGID`) */
|
||||
uint32_t gid;
|
||||
char *owner; /* set if openssh and version 4 */
|
||||
char *group; /* set if openssh and version 4 */
|
||||
|
||||
/**
|
||||
* File owner
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_OWNERGROUP`
|
||||
* or SFTP v3 with OpenSSH)
|
||||
*/
|
||||
char *owner;
|
||||
|
||||
/**
|
||||
* File group
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_OWNERGROUP`
|
||||
* or SFTP v3 with OpenSSH)
|
||||
*/
|
||||
char *group;
|
||||
|
||||
/** File permissions (requires flag `SSH_FILEXFER_ATTR_PERMISSIONS`) */
|
||||
uint32_t permissions;
|
||||
|
||||
/**
|
||||
* Access time
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_ACCESSTIME`)
|
||||
*/
|
||||
uint64_t atime64;
|
||||
|
||||
/**
|
||||
* Access time
|
||||
* (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_ACMODTIME`)
|
||||
*/
|
||||
uint32_t atime;
|
||||
|
||||
/**
|
||||
* Access time nanoseconds
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_SUBSECOND_TIMES`)
|
||||
*/
|
||||
uint32_t atime_nseconds;
|
||||
|
||||
/**
|
||||
* Creation time
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_CREATETIME`)
|
||||
*/
|
||||
uint64_t createtime;
|
||||
|
||||
/**
|
||||
* Creation time nanoseconds
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_SUBSECOND_TIMES`)
|
||||
*/
|
||||
uint32_t createtime_nseconds;
|
||||
|
||||
/**
|
||||
* Modification time
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_MODIFYTIME`)
|
||||
*/
|
||||
uint64_t mtime64;
|
||||
|
||||
/**
|
||||
* Modification time
|
||||
* (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_ACMODTIME`)
|
||||
*/
|
||||
uint32_t mtime;
|
||||
|
||||
/**
|
||||
* Modification time nanoseconds
|
||||
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_SUBSECOND_TIMES`)
|
||||
*/
|
||||
uint32_t mtime_nseconds;
|
||||
|
||||
/** ACL data (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_ACL`) */
|
||||
ssh_string acl;
|
||||
|
||||
/** Unused */
|
||||
uint32_t extended_count;
|
||||
|
||||
/** Unused */
|
||||
ssh_string extended_type;
|
||||
|
||||
/** Unused */
|
||||
ssh_string extended_data;
|
||||
};
|
||||
|
||||
@@ -206,27 +358,55 @@ struct sftp_attributes_struct {
|
||||
* @brief SFTP statvfs structure.
|
||||
*/
|
||||
struct sftp_statvfs_struct {
|
||||
uint64_t f_bsize; /** file system block size */
|
||||
uint64_t f_frsize; /** fundamental fs block size */
|
||||
uint64_t f_blocks; /** number of blocks (unit f_frsize) */
|
||||
uint64_t f_bfree; /** free blocks in file system */
|
||||
uint64_t f_bavail; /** free blocks for non-root */
|
||||
uint64_t f_files; /** total file inodes */
|
||||
uint64_t f_ffree; /** free file inodes */
|
||||
uint64_t f_favail; /** free file inodes for to non-root */
|
||||
uint64_t f_fsid; /** file system id */
|
||||
uint64_t f_flag; /** bit mask of f_flag values */
|
||||
uint64_t f_namemax; /** maximum filename length */
|
||||
/** file system block size */
|
||||
uint64_t f_bsize;
|
||||
|
||||
/** fundamental fs block size */
|
||||
uint64_t f_frsize;
|
||||
|
||||
/** number of blocks (unit f_frsize) */
|
||||
uint64_t f_blocks;
|
||||
|
||||
/** free blocks in file system */
|
||||
uint64_t f_bfree;
|
||||
|
||||
/** free blocks for non-root */
|
||||
uint64_t f_bavail;
|
||||
|
||||
/** total file inodes */
|
||||
uint64_t f_files;
|
||||
|
||||
/** free file inodes */
|
||||
uint64_t f_ffree;
|
||||
|
||||
/** free file inodes for non-root */
|
||||
uint64_t f_favail;
|
||||
|
||||
/** file system id */
|
||||
uint64_t f_fsid;
|
||||
|
||||
/** bit mask of f_flag values */
|
||||
uint64_t f_flag;
|
||||
|
||||
/** maximum filename length */
|
||||
uint64_t f_namemax;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SFTP limits structure.
|
||||
*/
|
||||
struct sftp_limits_struct {
|
||||
uint64_t max_packet_length; /** maximum number of bytes in a single sftp packet */
|
||||
uint64_t max_read_length; /** maximum length in a SSH_FXP_READ packet */
|
||||
uint64_t max_write_length; /** maximum length in a SSH_FXP_WRITE packet */
|
||||
uint64_t max_open_handles; /** maximum number of active handles allowed by server */
|
||||
/** maximum number of bytes in a single sftp packet */
|
||||
uint64_t max_packet_length;
|
||||
|
||||
/** maximum length in a SSH_FXP_READ packet */
|
||||
uint64_t max_read_length;
|
||||
|
||||
/** maximum length in a SSH_FXP_WRITE packet */
|
||||
uint64_t max_write_length;
|
||||
|
||||
/** maximum number of active handles allowed by server */
|
||||
uint64_t max_open_handles;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -279,7 +459,6 @@ LIBSSH_API sftp_session sftp_new(ssh_session session);
|
||||
*/
|
||||
LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Close and deallocate a sftp session.
|
||||
*
|
||||
@@ -1469,7 +1648,9 @@ LIBSSH_API void sftp_handle_remove(sftp_session sftp, void *handle);
|
||||
#define SFTP_EXTENDED SSH_FXP_EXTENDED
|
||||
|
||||
/* openssh flags */
|
||||
/** @brief statvfs flag: file system is mounted read-only. */
|
||||
#define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */
|
||||
/** @brief statvfs flag: file system does not support setuid/setgid. */
|
||||
#define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
#ifndef SFTP_PRIV_H
|
||||
#define SFTP_PRIV_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libssh/sftp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -43,17 +43,35 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Macro to declare an SFTP message callback function.
|
||||
*
|
||||
* @param name The name of the callback function to declare.
|
||||
*/
|
||||
#define SSH_SFTP_CALLBACK(name) \
|
||||
static int name(sftp_client_message message)
|
||||
|
||||
/**
|
||||
* @brief Callback for handling SFTP client messages.
|
||||
*
|
||||
* @param message The SFTP client message to handle.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
typedef int (*sftp_server_message_callback)(sftp_client_message message);
|
||||
|
||||
/**
|
||||
* @brief Maps an SFTP message type to its handler callback.
|
||||
*/
|
||||
struct sftp_message_handler
|
||||
{
|
||||
/** The name of the SFTP operation (e.g. "read", "write"). */
|
||||
const char *name;
|
||||
/** The extended operation name for SSH_FXP_EXTENDED requests, or NULL. */
|
||||
const char *extended_name;
|
||||
/** The SFTP message type code (e.g. SSH_FXP_READ). */
|
||||
uint8_t type;
|
||||
|
||||
/** The callback function to invoke for this message type. */
|
||||
sftp_server_message_callback cb;
|
||||
};
|
||||
|
||||
@@ -61,6 +79,7 @@ LIBSSH_API int sftp_channel_default_subsystem_request(ssh_session session,
|
||||
ssh_channel channel,
|
||||
const char *subsystem,
|
||||
void *userdata);
|
||||
|
||||
LIBSSH_API int sftp_channel_default_data_callback(ssh_session session,
|
||||
ssh_channel channel,
|
||||
void *data,
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
#ifndef WRAPPER_H_
|
||||
#define WRAPPER_H_
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/libcrypto.h"
|
||||
#include "libssh/libgcrypt.h"
|
||||
#include "libssh/libmbedcrypto.h"
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -1 +1 @@
|
||||
4.10.0
|
||||
4.11.0
|
||||
445
src/ABI/libssh-4.10.1.symbols
Normal file
445
src/ABI/libssh-4.10.1.symbols
Normal file
@@ -0,0 +1,445 @@
|
||||
_ssh_log
|
||||
buffer_free
|
||||
buffer_get
|
||||
buffer_get_len
|
||||
buffer_new
|
||||
channel_accept_x11
|
||||
channel_change_pty_size
|
||||
channel_close
|
||||
channel_forward_accept
|
||||
channel_forward_cancel
|
||||
channel_forward_listen
|
||||
channel_free
|
||||
channel_get_exit_status
|
||||
channel_get_session
|
||||
channel_is_closed
|
||||
channel_is_eof
|
||||
channel_is_open
|
||||
channel_new
|
||||
channel_open_forward
|
||||
channel_open_session
|
||||
channel_poll
|
||||
channel_read
|
||||
channel_read_buffer
|
||||
channel_read_nonblocking
|
||||
channel_request_env
|
||||
channel_request_exec
|
||||
channel_request_pty
|
||||
channel_request_pty_size
|
||||
channel_request_send_signal
|
||||
channel_request_sftp
|
||||
channel_request_shell
|
||||
channel_request_subsystem
|
||||
channel_request_x11
|
||||
channel_select
|
||||
channel_send_eof
|
||||
channel_set_blocking
|
||||
channel_write
|
||||
channel_write_stderr
|
||||
privatekey_free
|
||||
privatekey_from_file
|
||||
publickey_free
|
||||
publickey_from_file
|
||||
publickey_from_privatekey
|
||||
publickey_to_string
|
||||
sftp_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
sftp_chmod
|
||||
sftp_chown
|
||||
sftp_client_message_free
|
||||
sftp_client_message_get_data
|
||||
sftp_client_message_get_filename
|
||||
sftp_client_message_get_flags
|
||||
sftp_client_message_get_submessage
|
||||
sftp_client_message_get_type
|
||||
sftp_client_message_set_filename
|
||||
sftp_close
|
||||
sftp_closedir
|
||||
sftp_dir_eof
|
||||
sftp_expand_path
|
||||
sftp_extension_supported
|
||||
sftp_extensions_get_count
|
||||
sftp_extensions_get_data
|
||||
sftp_extensions_get_name
|
||||
sftp_file_set_blocking
|
||||
sftp_file_set_nonblocking
|
||||
sftp_free
|
||||
sftp_fstat
|
||||
sftp_fstatvfs
|
||||
sftp_fsync
|
||||
sftp_get_client_message
|
||||
sftp_get_error
|
||||
sftp_handle
|
||||
sftp_handle_alloc
|
||||
sftp_handle_remove
|
||||
sftp_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
sftp_lstat
|
||||
sftp_mkdir
|
||||
sftp_new
|
||||
sftp_new_channel
|
||||
sftp_open
|
||||
sftp_opendir
|
||||
sftp_read
|
||||
sftp_readdir
|
||||
sftp_readlink
|
||||
sftp_rename
|
||||
sftp_reply_attr
|
||||
sftp_reply_data
|
||||
sftp_reply_handle
|
||||
sftp_reply_name
|
||||
sftp_reply_names
|
||||
sftp_reply_names_add
|
||||
sftp_reply_status
|
||||
sftp_rewind
|
||||
sftp_rmdir
|
||||
sftp_seek
|
||||
sftp_seek64
|
||||
sftp_send_client_message
|
||||
sftp_server_free
|
||||
sftp_server_init
|
||||
sftp_server_new
|
||||
sftp_server_version
|
||||
sftp_setstat
|
||||
sftp_stat
|
||||
sftp_statvfs
|
||||
sftp_statvfs_free
|
||||
sftp_symlink
|
||||
sftp_tell
|
||||
sftp_tell64
|
||||
sftp_unlink
|
||||
sftp_utimes
|
||||
sftp_write
|
||||
ssh_accept
|
||||
ssh_add_channel_callbacks
|
||||
ssh_auth_list
|
||||
ssh_basename
|
||||
ssh_bind_accept
|
||||
ssh_bind_accept_fd
|
||||
ssh_bind_fd_toaccept
|
||||
ssh_bind_free
|
||||
ssh_bind_get_fd
|
||||
ssh_bind_listen
|
||||
ssh_bind_new
|
||||
ssh_bind_options_parse_config
|
||||
ssh_bind_options_set
|
||||
ssh_bind_set_blocking
|
||||
ssh_bind_set_callbacks
|
||||
ssh_bind_set_fd
|
||||
ssh_blocking_flush
|
||||
ssh_buffer_add_data
|
||||
ssh_buffer_free
|
||||
ssh_buffer_get
|
||||
ssh_buffer_get_data
|
||||
ssh_buffer_get_len
|
||||
ssh_buffer_new
|
||||
ssh_buffer_reinit
|
||||
ssh_channel_accept_forward
|
||||
ssh_channel_accept_x11
|
||||
ssh_channel_cancel_forward
|
||||
ssh_channel_change_pty_size
|
||||
ssh_channel_close
|
||||
ssh_channel_free
|
||||
ssh_channel_get_exit_state
|
||||
ssh_channel_get_exit_status
|
||||
ssh_channel_get_session
|
||||
ssh_channel_is_closed
|
||||
ssh_channel_is_eof
|
||||
ssh_channel_is_open
|
||||
ssh_channel_listen_forward
|
||||
ssh_channel_new
|
||||
ssh_channel_open_auth_agent
|
||||
ssh_channel_open_forward
|
||||
ssh_channel_open_forward_port
|
||||
ssh_channel_open_forward_unix
|
||||
ssh_channel_open_reverse_forward
|
||||
ssh_channel_open_session
|
||||
ssh_channel_open_x11
|
||||
ssh_channel_poll
|
||||
ssh_channel_poll_timeout
|
||||
ssh_channel_read
|
||||
ssh_channel_read_nonblocking
|
||||
ssh_channel_read_timeout
|
||||
ssh_channel_request_auth_agent
|
||||
ssh_channel_request_env
|
||||
ssh_channel_request_exec
|
||||
ssh_channel_request_pty
|
||||
ssh_channel_request_pty_size
|
||||
ssh_channel_request_pty_size_modes
|
||||
ssh_channel_request_send_break
|
||||
ssh_channel_request_send_exit_signal
|
||||
ssh_channel_request_send_exit_status
|
||||
ssh_channel_request_send_signal
|
||||
ssh_channel_request_sftp
|
||||
ssh_channel_request_shell
|
||||
ssh_channel_request_subsystem
|
||||
ssh_channel_request_x11
|
||||
ssh_channel_select
|
||||
ssh_channel_send_eof
|
||||
ssh_channel_set_blocking
|
||||
ssh_channel_set_counter
|
||||
ssh_channel_window_size
|
||||
ssh_channel_write
|
||||
ssh_channel_write_stderr
|
||||
ssh_clean_pubkey_hash
|
||||
ssh_connect
|
||||
ssh_connector_free
|
||||
ssh_connector_new
|
||||
ssh_connector_set_in_channel
|
||||
ssh_connector_set_in_fd
|
||||
ssh_connector_set_out_channel
|
||||
ssh_connector_set_out_fd
|
||||
ssh_copyright
|
||||
ssh_dirname
|
||||
ssh_disconnect
|
||||
ssh_dump_knownhost
|
||||
ssh_event_add_connector
|
||||
ssh_event_add_fd
|
||||
ssh_event_add_session
|
||||
ssh_event_dopoll
|
||||
ssh_event_free
|
||||
ssh_event_new
|
||||
ssh_event_remove_connector
|
||||
ssh_event_remove_fd
|
||||
ssh_event_remove_session
|
||||
ssh_execute_message_callbacks
|
||||
ssh_finalize
|
||||
ssh_forward_accept
|
||||
ssh_forward_cancel
|
||||
ssh_forward_listen
|
||||
ssh_free
|
||||
ssh_get_cipher_in
|
||||
ssh_get_cipher_out
|
||||
ssh_get_clientbanner
|
||||
ssh_get_disconnect_message
|
||||
ssh_get_error
|
||||
ssh_get_error_code
|
||||
ssh_get_fd
|
||||
ssh_get_fingerprint_hash
|
||||
ssh_get_hexa
|
||||
ssh_get_hmac_in
|
||||
ssh_get_hmac_out
|
||||
ssh_get_issue_banner
|
||||
ssh_get_kex_algo
|
||||
ssh_get_log_callback
|
||||
ssh_get_log_level
|
||||
ssh_get_log_userdata
|
||||
ssh_get_openssh_version
|
||||
ssh_get_poll_flags
|
||||
ssh_get_pubkey
|
||||
ssh_get_pubkey_hash
|
||||
ssh_get_publickey
|
||||
ssh_get_publickey_hash
|
||||
ssh_get_random
|
||||
ssh_get_server_publickey
|
||||
ssh_get_serverbanner
|
||||
ssh_get_status
|
||||
ssh_get_version
|
||||
ssh_getpass
|
||||
ssh_gssapi_get_creds
|
||||
ssh_gssapi_set_creds
|
||||
ssh_handle_key_exchange
|
||||
ssh_init
|
||||
ssh_is_blocking
|
||||
ssh_is_connected
|
||||
ssh_is_server_known
|
||||
ssh_key_cmp
|
||||
ssh_key_dup
|
||||
ssh_key_free
|
||||
ssh_key_is_private
|
||||
ssh_key_is_public
|
||||
ssh_key_new
|
||||
ssh_key_type
|
||||
ssh_key_type_from_name
|
||||
ssh_key_type_to_char
|
||||
ssh_known_hosts_parse_line
|
||||
ssh_knownhosts_entry_free
|
||||
ssh_log
|
||||
ssh_message_auth_interactive_request
|
||||
ssh_message_auth_kbdint_is_response
|
||||
ssh_message_auth_password
|
||||
ssh_message_auth_pubkey
|
||||
ssh_message_auth_publickey
|
||||
ssh_message_auth_publickey_state
|
||||
ssh_message_auth_reply_pk_ok
|
||||
ssh_message_auth_reply_pk_ok_simple
|
||||
ssh_message_auth_reply_success
|
||||
ssh_message_auth_set_methods
|
||||
ssh_message_auth_user
|
||||
ssh_message_channel_request_channel
|
||||
ssh_message_channel_request_command
|
||||
ssh_message_channel_request_env_name
|
||||
ssh_message_channel_request_env_value
|
||||
ssh_message_channel_request_open_destination
|
||||
ssh_message_channel_request_open_destination_port
|
||||
ssh_message_channel_request_open_originator
|
||||
ssh_message_channel_request_open_originator_port
|
||||
ssh_message_channel_request_open_reply_accept
|
||||
ssh_message_channel_request_open_reply_accept_channel
|
||||
ssh_message_channel_request_pty_height
|
||||
ssh_message_channel_request_pty_pxheight
|
||||
ssh_message_channel_request_pty_pxwidth
|
||||
ssh_message_channel_request_pty_term
|
||||
ssh_message_channel_request_pty_width
|
||||
ssh_message_channel_request_reply_success
|
||||
ssh_message_channel_request_subsystem
|
||||
ssh_message_channel_request_x11_auth_cookie
|
||||
ssh_message_channel_request_x11_auth_protocol
|
||||
ssh_message_channel_request_x11_screen_number
|
||||
ssh_message_channel_request_x11_single_connection
|
||||
ssh_message_free
|
||||
ssh_message_get
|
||||
ssh_message_global_request_address
|
||||
ssh_message_global_request_port
|
||||
ssh_message_global_request_reply_success
|
||||
ssh_message_reply_default
|
||||
ssh_message_retrieve
|
||||
ssh_message_service_reply_success
|
||||
ssh_message_service_service
|
||||
ssh_message_subtype
|
||||
ssh_message_type
|
||||
ssh_mkdir
|
||||
ssh_new
|
||||
ssh_options_copy
|
||||
ssh_options_get
|
||||
ssh_options_get_port
|
||||
ssh_options_getopt
|
||||
ssh_options_parse_config
|
||||
ssh_options_set
|
||||
ssh_pcap_file_close
|
||||
ssh_pcap_file_free
|
||||
ssh_pcap_file_new
|
||||
ssh_pcap_file_open
|
||||
ssh_pki_copy_cert_to_privkey
|
||||
ssh_pki_export_privkey_base64
|
||||
ssh_pki_export_privkey_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
ssh_pki_export_privkey_to_pubkey
|
||||
ssh_pki_export_pubkey_base64
|
||||
ssh_pki_export_pubkey_file
|
||||
ssh_pki_generate
|
||||
ssh_pki_import_cert_base64
|
||||
ssh_pki_import_cert_file
|
||||
ssh_pki_import_privkey_base64
|
||||
ssh_pki_import_privkey_file
|
||||
ssh_pki_import_pubkey_base64
|
||||
ssh_pki_import_pubkey_file
|
||||
ssh_pki_key_ecdsa_name
|
||||
ssh_print_hash
|
||||
ssh_print_hexa
|
||||
ssh_privatekey_type
|
||||
ssh_publickey_to_file
|
||||
ssh_remove_channel_callbacks
|
||||
ssh_request_no_more_sessions
|
||||
ssh_scp_accept_request
|
||||
ssh_scp_close
|
||||
ssh_scp_deny_request
|
||||
ssh_scp_free
|
||||
ssh_scp_init
|
||||
ssh_scp_leave_directory
|
||||
ssh_scp_new
|
||||
ssh_scp_pull_request
|
||||
ssh_scp_push_directory
|
||||
ssh_scp_push_file
|
||||
ssh_scp_push_file64
|
||||
ssh_scp_read
|
||||
ssh_scp_request_get_filename
|
||||
ssh_scp_request_get_permissions
|
||||
ssh_scp_request_get_size
|
||||
ssh_scp_request_get_size64
|
||||
ssh_scp_request_get_warning
|
||||
ssh_scp_write
|
||||
ssh_select
|
||||
ssh_send_debug
|
||||
ssh_send_ignore
|
||||
ssh_send_issue_banner
|
||||
ssh_send_keepalive
|
||||
ssh_server_init_kex
|
||||
ssh_service_request
|
||||
ssh_session_export_known_hosts_entry
|
||||
ssh_session_get_known_hosts_entry
|
||||
ssh_session_has_known_hosts_entry
|
||||
ssh_session_is_known_server
|
||||
ssh_session_set_disconnect_message
|
||||
ssh_session_update_known_hosts
|
||||
ssh_set_agent_channel
|
||||
ssh_set_agent_socket
|
||||
ssh_set_auth_methods
|
||||
ssh_set_blocking
|
||||
ssh_set_callbacks
|
||||
ssh_set_channel_callbacks
|
||||
ssh_set_counters
|
||||
ssh_set_fd_except
|
||||
ssh_set_fd_toread
|
||||
ssh_set_fd_towrite
|
||||
ssh_set_log_callback
|
||||
ssh_set_log_level
|
||||
ssh_set_log_userdata
|
||||
ssh_set_message_callback
|
||||
ssh_set_pcap_file
|
||||
ssh_set_server_callbacks
|
||||
ssh_silent_disconnect
|
||||
ssh_string_burn
|
||||
ssh_string_copy
|
||||
ssh_string_data
|
||||
ssh_string_fill
|
||||
ssh_string_free
|
||||
ssh_string_free_char
|
||||
ssh_string_from_char
|
||||
ssh_string_get_char
|
||||
ssh_string_len
|
||||
ssh_string_new
|
||||
ssh_string_to_char
|
||||
ssh_threads_get_default
|
||||
ssh_threads_get_noop
|
||||
ssh_threads_get_pthread
|
||||
ssh_threads_set_callbacks
|
||||
ssh_try_publickey_from_file
|
||||
ssh_userauth_agent
|
||||
ssh_userauth_agent_pubkey
|
||||
ssh_userauth_autopubkey
|
||||
ssh_userauth_gssapi
|
||||
ssh_userauth_kbdint
|
||||
ssh_userauth_kbdint_getanswer
|
||||
ssh_userauth_kbdint_getinstruction
|
||||
ssh_userauth_kbdint_getname
|
||||
ssh_userauth_kbdint_getnanswers
|
||||
ssh_userauth_kbdint_getnprompts
|
||||
ssh_userauth_kbdint_getprompt
|
||||
ssh_userauth_kbdint_setanswer
|
||||
ssh_userauth_list
|
||||
ssh_userauth_none
|
||||
ssh_userauth_offer_pubkey
|
||||
ssh_userauth_password
|
||||
ssh_userauth_privatekey_file
|
||||
ssh_userauth_pubkey
|
||||
ssh_userauth_publickey
|
||||
ssh_userauth_publickey_auto
|
||||
ssh_userauth_publickey_auto_get_current_identity
|
||||
ssh_userauth_try_publickey
|
||||
ssh_version
|
||||
ssh_vlog
|
||||
ssh_write_knownhost
|
||||
string_burn
|
||||
string_copy
|
||||
string_data
|
||||
string_fill
|
||||
string_free
|
||||
string_from_char
|
||||
string_len
|
||||
string_new
|
||||
string_to_char
|
||||
445
src/ABI/libssh-4.10.2.symbols
Normal file
445
src/ABI/libssh-4.10.2.symbols
Normal file
@@ -0,0 +1,445 @@
|
||||
_ssh_log
|
||||
buffer_free
|
||||
buffer_get
|
||||
buffer_get_len
|
||||
buffer_new
|
||||
channel_accept_x11
|
||||
channel_change_pty_size
|
||||
channel_close
|
||||
channel_forward_accept
|
||||
channel_forward_cancel
|
||||
channel_forward_listen
|
||||
channel_free
|
||||
channel_get_exit_status
|
||||
channel_get_session
|
||||
channel_is_closed
|
||||
channel_is_eof
|
||||
channel_is_open
|
||||
channel_new
|
||||
channel_open_forward
|
||||
channel_open_session
|
||||
channel_poll
|
||||
channel_read
|
||||
channel_read_buffer
|
||||
channel_read_nonblocking
|
||||
channel_request_env
|
||||
channel_request_exec
|
||||
channel_request_pty
|
||||
channel_request_pty_size
|
||||
channel_request_send_signal
|
||||
channel_request_sftp
|
||||
channel_request_shell
|
||||
channel_request_subsystem
|
||||
channel_request_x11
|
||||
channel_select
|
||||
channel_send_eof
|
||||
channel_set_blocking
|
||||
channel_write
|
||||
channel_write_stderr
|
||||
privatekey_free
|
||||
privatekey_from_file
|
||||
publickey_free
|
||||
publickey_from_file
|
||||
publickey_from_privatekey
|
||||
publickey_to_string
|
||||
sftp_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
sftp_chmod
|
||||
sftp_chown
|
||||
sftp_client_message_free
|
||||
sftp_client_message_get_data
|
||||
sftp_client_message_get_filename
|
||||
sftp_client_message_get_flags
|
||||
sftp_client_message_get_submessage
|
||||
sftp_client_message_get_type
|
||||
sftp_client_message_set_filename
|
||||
sftp_close
|
||||
sftp_closedir
|
||||
sftp_dir_eof
|
||||
sftp_expand_path
|
||||
sftp_extension_supported
|
||||
sftp_extensions_get_count
|
||||
sftp_extensions_get_data
|
||||
sftp_extensions_get_name
|
||||
sftp_file_set_blocking
|
||||
sftp_file_set_nonblocking
|
||||
sftp_free
|
||||
sftp_fstat
|
||||
sftp_fstatvfs
|
||||
sftp_fsync
|
||||
sftp_get_client_message
|
||||
sftp_get_error
|
||||
sftp_handle
|
||||
sftp_handle_alloc
|
||||
sftp_handle_remove
|
||||
sftp_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
sftp_lstat
|
||||
sftp_mkdir
|
||||
sftp_new
|
||||
sftp_new_channel
|
||||
sftp_open
|
||||
sftp_opendir
|
||||
sftp_read
|
||||
sftp_readdir
|
||||
sftp_readlink
|
||||
sftp_rename
|
||||
sftp_reply_attr
|
||||
sftp_reply_data
|
||||
sftp_reply_handle
|
||||
sftp_reply_name
|
||||
sftp_reply_names
|
||||
sftp_reply_names_add
|
||||
sftp_reply_status
|
||||
sftp_rewind
|
||||
sftp_rmdir
|
||||
sftp_seek
|
||||
sftp_seek64
|
||||
sftp_send_client_message
|
||||
sftp_server_free
|
||||
sftp_server_init
|
||||
sftp_server_new
|
||||
sftp_server_version
|
||||
sftp_setstat
|
||||
sftp_stat
|
||||
sftp_statvfs
|
||||
sftp_statvfs_free
|
||||
sftp_symlink
|
||||
sftp_tell
|
||||
sftp_tell64
|
||||
sftp_unlink
|
||||
sftp_utimes
|
||||
sftp_write
|
||||
ssh_accept
|
||||
ssh_add_channel_callbacks
|
||||
ssh_auth_list
|
||||
ssh_basename
|
||||
ssh_bind_accept
|
||||
ssh_bind_accept_fd
|
||||
ssh_bind_fd_toaccept
|
||||
ssh_bind_free
|
||||
ssh_bind_get_fd
|
||||
ssh_bind_listen
|
||||
ssh_bind_new
|
||||
ssh_bind_options_parse_config
|
||||
ssh_bind_options_set
|
||||
ssh_bind_set_blocking
|
||||
ssh_bind_set_callbacks
|
||||
ssh_bind_set_fd
|
||||
ssh_blocking_flush
|
||||
ssh_buffer_add_data
|
||||
ssh_buffer_free
|
||||
ssh_buffer_get
|
||||
ssh_buffer_get_data
|
||||
ssh_buffer_get_len
|
||||
ssh_buffer_new
|
||||
ssh_buffer_reinit
|
||||
ssh_channel_accept_forward
|
||||
ssh_channel_accept_x11
|
||||
ssh_channel_cancel_forward
|
||||
ssh_channel_change_pty_size
|
||||
ssh_channel_close
|
||||
ssh_channel_free
|
||||
ssh_channel_get_exit_state
|
||||
ssh_channel_get_exit_status
|
||||
ssh_channel_get_session
|
||||
ssh_channel_is_closed
|
||||
ssh_channel_is_eof
|
||||
ssh_channel_is_open
|
||||
ssh_channel_listen_forward
|
||||
ssh_channel_new
|
||||
ssh_channel_open_auth_agent
|
||||
ssh_channel_open_forward
|
||||
ssh_channel_open_forward_port
|
||||
ssh_channel_open_forward_unix
|
||||
ssh_channel_open_reverse_forward
|
||||
ssh_channel_open_session
|
||||
ssh_channel_open_x11
|
||||
ssh_channel_poll
|
||||
ssh_channel_poll_timeout
|
||||
ssh_channel_read
|
||||
ssh_channel_read_nonblocking
|
||||
ssh_channel_read_timeout
|
||||
ssh_channel_request_auth_agent
|
||||
ssh_channel_request_env
|
||||
ssh_channel_request_exec
|
||||
ssh_channel_request_pty
|
||||
ssh_channel_request_pty_size
|
||||
ssh_channel_request_pty_size_modes
|
||||
ssh_channel_request_send_break
|
||||
ssh_channel_request_send_exit_signal
|
||||
ssh_channel_request_send_exit_status
|
||||
ssh_channel_request_send_signal
|
||||
ssh_channel_request_sftp
|
||||
ssh_channel_request_shell
|
||||
ssh_channel_request_subsystem
|
||||
ssh_channel_request_x11
|
||||
ssh_channel_select
|
||||
ssh_channel_send_eof
|
||||
ssh_channel_set_blocking
|
||||
ssh_channel_set_counter
|
||||
ssh_channel_window_size
|
||||
ssh_channel_write
|
||||
ssh_channel_write_stderr
|
||||
ssh_clean_pubkey_hash
|
||||
ssh_connect
|
||||
ssh_connector_free
|
||||
ssh_connector_new
|
||||
ssh_connector_set_in_channel
|
||||
ssh_connector_set_in_fd
|
||||
ssh_connector_set_out_channel
|
||||
ssh_connector_set_out_fd
|
||||
ssh_copyright
|
||||
ssh_dirname
|
||||
ssh_disconnect
|
||||
ssh_dump_knownhost
|
||||
ssh_event_add_connector
|
||||
ssh_event_add_fd
|
||||
ssh_event_add_session
|
||||
ssh_event_dopoll
|
||||
ssh_event_free
|
||||
ssh_event_new
|
||||
ssh_event_remove_connector
|
||||
ssh_event_remove_fd
|
||||
ssh_event_remove_session
|
||||
ssh_execute_message_callbacks
|
||||
ssh_finalize
|
||||
ssh_forward_accept
|
||||
ssh_forward_cancel
|
||||
ssh_forward_listen
|
||||
ssh_free
|
||||
ssh_get_cipher_in
|
||||
ssh_get_cipher_out
|
||||
ssh_get_clientbanner
|
||||
ssh_get_disconnect_message
|
||||
ssh_get_error
|
||||
ssh_get_error_code
|
||||
ssh_get_fd
|
||||
ssh_get_fingerprint_hash
|
||||
ssh_get_hexa
|
||||
ssh_get_hmac_in
|
||||
ssh_get_hmac_out
|
||||
ssh_get_issue_banner
|
||||
ssh_get_kex_algo
|
||||
ssh_get_log_callback
|
||||
ssh_get_log_level
|
||||
ssh_get_log_userdata
|
||||
ssh_get_openssh_version
|
||||
ssh_get_poll_flags
|
||||
ssh_get_pubkey
|
||||
ssh_get_pubkey_hash
|
||||
ssh_get_publickey
|
||||
ssh_get_publickey_hash
|
||||
ssh_get_random
|
||||
ssh_get_server_publickey
|
||||
ssh_get_serverbanner
|
||||
ssh_get_status
|
||||
ssh_get_version
|
||||
ssh_getpass
|
||||
ssh_gssapi_get_creds
|
||||
ssh_gssapi_set_creds
|
||||
ssh_handle_key_exchange
|
||||
ssh_init
|
||||
ssh_is_blocking
|
||||
ssh_is_connected
|
||||
ssh_is_server_known
|
||||
ssh_key_cmp
|
||||
ssh_key_dup
|
||||
ssh_key_free
|
||||
ssh_key_is_private
|
||||
ssh_key_is_public
|
||||
ssh_key_new
|
||||
ssh_key_type
|
||||
ssh_key_type_from_name
|
||||
ssh_key_type_to_char
|
||||
ssh_known_hosts_parse_line
|
||||
ssh_knownhosts_entry_free
|
||||
ssh_log
|
||||
ssh_message_auth_interactive_request
|
||||
ssh_message_auth_kbdint_is_response
|
||||
ssh_message_auth_password
|
||||
ssh_message_auth_pubkey
|
||||
ssh_message_auth_publickey
|
||||
ssh_message_auth_publickey_state
|
||||
ssh_message_auth_reply_pk_ok
|
||||
ssh_message_auth_reply_pk_ok_simple
|
||||
ssh_message_auth_reply_success
|
||||
ssh_message_auth_set_methods
|
||||
ssh_message_auth_user
|
||||
ssh_message_channel_request_channel
|
||||
ssh_message_channel_request_command
|
||||
ssh_message_channel_request_env_name
|
||||
ssh_message_channel_request_env_value
|
||||
ssh_message_channel_request_open_destination
|
||||
ssh_message_channel_request_open_destination_port
|
||||
ssh_message_channel_request_open_originator
|
||||
ssh_message_channel_request_open_originator_port
|
||||
ssh_message_channel_request_open_reply_accept
|
||||
ssh_message_channel_request_open_reply_accept_channel
|
||||
ssh_message_channel_request_pty_height
|
||||
ssh_message_channel_request_pty_pxheight
|
||||
ssh_message_channel_request_pty_pxwidth
|
||||
ssh_message_channel_request_pty_term
|
||||
ssh_message_channel_request_pty_width
|
||||
ssh_message_channel_request_reply_success
|
||||
ssh_message_channel_request_subsystem
|
||||
ssh_message_channel_request_x11_auth_cookie
|
||||
ssh_message_channel_request_x11_auth_protocol
|
||||
ssh_message_channel_request_x11_screen_number
|
||||
ssh_message_channel_request_x11_single_connection
|
||||
ssh_message_free
|
||||
ssh_message_get
|
||||
ssh_message_global_request_address
|
||||
ssh_message_global_request_port
|
||||
ssh_message_global_request_reply_success
|
||||
ssh_message_reply_default
|
||||
ssh_message_retrieve
|
||||
ssh_message_service_reply_success
|
||||
ssh_message_service_service
|
||||
ssh_message_subtype
|
||||
ssh_message_type
|
||||
ssh_mkdir
|
||||
ssh_new
|
||||
ssh_options_copy
|
||||
ssh_options_get
|
||||
ssh_options_get_port
|
||||
ssh_options_getopt
|
||||
ssh_options_parse_config
|
||||
ssh_options_set
|
||||
ssh_pcap_file_close
|
||||
ssh_pcap_file_free
|
||||
ssh_pcap_file_new
|
||||
ssh_pcap_file_open
|
||||
ssh_pki_copy_cert_to_privkey
|
||||
ssh_pki_export_privkey_base64
|
||||
ssh_pki_export_privkey_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
ssh_pki_export_privkey_to_pubkey
|
||||
ssh_pki_export_pubkey_base64
|
||||
ssh_pki_export_pubkey_file
|
||||
ssh_pki_generate
|
||||
ssh_pki_import_cert_base64
|
||||
ssh_pki_import_cert_file
|
||||
ssh_pki_import_privkey_base64
|
||||
ssh_pki_import_privkey_file
|
||||
ssh_pki_import_pubkey_base64
|
||||
ssh_pki_import_pubkey_file
|
||||
ssh_pki_key_ecdsa_name
|
||||
ssh_print_hash
|
||||
ssh_print_hexa
|
||||
ssh_privatekey_type
|
||||
ssh_publickey_to_file
|
||||
ssh_remove_channel_callbacks
|
||||
ssh_request_no_more_sessions
|
||||
ssh_scp_accept_request
|
||||
ssh_scp_close
|
||||
ssh_scp_deny_request
|
||||
ssh_scp_free
|
||||
ssh_scp_init
|
||||
ssh_scp_leave_directory
|
||||
ssh_scp_new
|
||||
ssh_scp_pull_request
|
||||
ssh_scp_push_directory
|
||||
ssh_scp_push_file
|
||||
ssh_scp_push_file64
|
||||
ssh_scp_read
|
||||
ssh_scp_request_get_filename
|
||||
ssh_scp_request_get_permissions
|
||||
ssh_scp_request_get_size
|
||||
ssh_scp_request_get_size64
|
||||
ssh_scp_request_get_warning
|
||||
ssh_scp_write
|
||||
ssh_select
|
||||
ssh_send_debug
|
||||
ssh_send_ignore
|
||||
ssh_send_issue_banner
|
||||
ssh_send_keepalive
|
||||
ssh_server_init_kex
|
||||
ssh_service_request
|
||||
ssh_session_export_known_hosts_entry
|
||||
ssh_session_get_known_hosts_entry
|
||||
ssh_session_has_known_hosts_entry
|
||||
ssh_session_is_known_server
|
||||
ssh_session_set_disconnect_message
|
||||
ssh_session_update_known_hosts
|
||||
ssh_set_agent_channel
|
||||
ssh_set_agent_socket
|
||||
ssh_set_auth_methods
|
||||
ssh_set_blocking
|
||||
ssh_set_callbacks
|
||||
ssh_set_channel_callbacks
|
||||
ssh_set_counters
|
||||
ssh_set_fd_except
|
||||
ssh_set_fd_toread
|
||||
ssh_set_fd_towrite
|
||||
ssh_set_log_callback
|
||||
ssh_set_log_level
|
||||
ssh_set_log_userdata
|
||||
ssh_set_message_callback
|
||||
ssh_set_pcap_file
|
||||
ssh_set_server_callbacks
|
||||
ssh_silent_disconnect
|
||||
ssh_string_burn
|
||||
ssh_string_copy
|
||||
ssh_string_data
|
||||
ssh_string_fill
|
||||
ssh_string_free
|
||||
ssh_string_free_char
|
||||
ssh_string_from_char
|
||||
ssh_string_get_char
|
||||
ssh_string_len
|
||||
ssh_string_new
|
||||
ssh_string_to_char
|
||||
ssh_threads_get_default
|
||||
ssh_threads_get_noop
|
||||
ssh_threads_get_pthread
|
||||
ssh_threads_set_callbacks
|
||||
ssh_try_publickey_from_file
|
||||
ssh_userauth_agent
|
||||
ssh_userauth_agent_pubkey
|
||||
ssh_userauth_autopubkey
|
||||
ssh_userauth_gssapi
|
||||
ssh_userauth_kbdint
|
||||
ssh_userauth_kbdint_getanswer
|
||||
ssh_userauth_kbdint_getinstruction
|
||||
ssh_userauth_kbdint_getname
|
||||
ssh_userauth_kbdint_getnanswers
|
||||
ssh_userauth_kbdint_getnprompts
|
||||
ssh_userauth_kbdint_getprompt
|
||||
ssh_userauth_kbdint_setanswer
|
||||
ssh_userauth_list
|
||||
ssh_userauth_none
|
||||
ssh_userauth_offer_pubkey
|
||||
ssh_userauth_password
|
||||
ssh_userauth_privatekey_file
|
||||
ssh_userauth_pubkey
|
||||
ssh_userauth_publickey
|
||||
ssh_userauth_publickey_auto
|
||||
ssh_userauth_publickey_auto_get_current_identity
|
||||
ssh_userauth_try_publickey
|
||||
ssh_version
|
||||
ssh_vlog
|
||||
ssh_write_knownhost
|
||||
string_burn
|
||||
string_copy
|
||||
string_data
|
||||
string_fill
|
||||
string_free
|
||||
string_from_char
|
||||
string_len
|
||||
string_new
|
||||
string_to_char
|
||||
445
src/ABI/libssh-4.10.3.symbols
Normal file
445
src/ABI/libssh-4.10.3.symbols
Normal file
@@ -0,0 +1,445 @@
|
||||
_ssh_log
|
||||
buffer_free
|
||||
buffer_get
|
||||
buffer_get_len
|
||||
buffer_new
|
||||
channel_accept_x11
|
||||
channel_change_pty_size
|
||||
channel_close
|
||||
channel_forward_accept
|
||||
channel_forward_cancel
|
||||
channel_forward_listen
|
||||
channel_free
|
||||
channel_get_exit_status
|
||||
channel_get_session
|
||||
channel_is_closed
|
||||
channel_is_eof
|
||||
channel_is_open
|
||||
channel_new
|
||||
channel_open_forward
|
||||
channel_open_session
|
||||
channel_poll
|
||||
channel_read
|
||||
channel_read_buffer
|
||||
channel_read_nonblocking
|
||||
channel_request_env
|
||||
channel_request_exec
|
||||
channel_request_pty
|
||||
channel_request_pty_size
|
||||
channel_request_send_signal
|
||||
channel_request_sftp
|
||||
channel_request_shell
|
||||
channel_request_subsystem
|
||||
channel_request_x11
|
||||
channel_select
|
||||
channel_send_eof
|
||||
channel_set_blocking
|
||||
channel_write
|
||||
channel_write_stderr
|
||||
privatekey_free
|
||||
privatekey_from_file
|
||||
publickey_free
|
||||
publickey_from_file
|
||||
publickey_from_privatekey
|
||||
publickey_to_string
|
||||
sftp_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
sftp_chmod
|
||||
sftp_chown
|
||||
sftp_client_message_free
|
||||
sftp_client_message_get_data
|
||||
sftp_client_message_get_filename
|
||||
sftp_client_message_get_flags
|
||||
sftp_client_message_get_submessage
|
||||
sftp_client_message_get_type
|
||||
sftp_client_message_set_filename
|
||||
sftp_close
|
||||
sftp_closedir
|
||||
sftp_dir_eof
|
||||
sftp_expand_path
|
||||
sftp_extension_supported
|
||||
sftp_extensions_get_count
|
||||
sftp_extensions_get_data
|
||||
sftp_extensions_get_name
|
||||
sftp_file_set_blocking
|
||||
sftp_file_set_nonblocking
|
||||
sftp_free
|
||||
sftp_fstat
|
||||
sftp_fstatvfs
|
||||
sftp_fsync
|
||||
sftp_get_client_message
|
||||
sftp_get_error
|
||||
sftp_handle
|
||||
sftp_handle_alloc
|
||||
sftp_handle_remove
|
||||
sftp_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
sftp_lstat
|
||||
sftp_mkdir
|
||||
sftp_new
|
||||
sftp_new_channel
|
||||
sftp_open
|
||||
sftp_opendir
|
||||
sftp_read
|
||||
sftp_readdir
|
||||
sftp_readlink
|
||||
sftp_rename
|
||||
sftp_reply_attr
|
||||
sftp_reply_data
|
||||
sftp_reply_handle
|
||||
sftp_reply_name
|
||||
sftp_reply_names
|
||||
sftp_reply_names_add
|
||||
sftp_reply_status
|
||||
sftp_rewind
|
||||
sftp_rmdir
|
||||
sftp_seek
|
||||
sftp_seek64
|
||||
sftp_send_client_message
|
||||
sftp_server_free
|
||||
sftp_server_init
|
||||
sftp_server_new
|
||||
sftp_server_version
|
||||
sftp_setstat
|
||||
sftp_stat
|
||||
sftp_statvfs
|
||||
sftp_statvfs_free
|
||||
sftp_symlink
|
||||
sftp_tell
|
||||
sftp_tell64
|
||||
sftp_unlink
|
||||
sftp_utimes
|
||||
sftp_write
|
||||
ssh_accept
|
||||
ssh_add_channel_callbacks
|
||||
ssh_auth_list
|
||||
ssh_basename
|
||||
ssh_bind_accept
|
||||
ssh_bind_accept_fd
|
||||
ssh_bind_fd_toaccept
|
||||
ssh_bind_free
|
||||
ssh_bind_get_fd
|
||||
ssh_bind_listen
|
||||
ssh_bind_new
|
||||
ssh_bind_options_parse_config
|
||||
ssh_bind_options_set
|
||||
ssh_bind_set_blocking
|
||||
ssh_bind_set_callbacks
|
||||
ssh_bind_set_fd
|
||||
ssh_blocking_flush
|
||||
ssh_buffer_add_data
|
||||
ssh_buffer_free
|
||||
ssh_buffer_get
|
||||
ssh_buffer_get_data
|
||||
ssh_buffer_get_len
|
||||
ssh_buffer_new
|
||||
ssh_buffer_reinit
|
||||
ssh_channel_accept_forward
|
||||
ssh_channel_accept_x11
|
||||
ssh_channel_cancel_forward
|
||||
ssh_channel_change_pty_size
|
||||
ssh_channel_close
|
||||
ssh_channel_free
|
||||
ssh_channel_get_exit_state
|
||||
ssh_channel_get_exit_status
|
||||
ssh_channel_get_session
|
||||
ssh_channel_is_closed
|
||||
ssh_channel_is_eof
|
||||
ssh_channel_is_open
|
||||
ssh_channel_listen_forward
|
||||
ssh_channel_new
|
||||
ssh_channel_open_auth_agent
|
||||
ssh_channel_open_forward
|
||||
ssh_channel_open_forward_port
|
||||
ssh_channel_open_forward_unix
|
||||
ssh_channel_open_reverse_forward
|
||||
ssh_channel_open_session
|
||||
ssh_channel_open_x11
|
||||
ssh_channel_poll
|
||||
ssh_channel_poll_timeout
|
||||
ssh_channel_read
|
||||
ssh_channel_read_nonblocking
|
||||
ssh_channel_read_timeout
|
||||
ssh_channel_request_auth_agent
|
||||
ssh_channel_request_env
|
||||
ssh_channel_request_exec
|
||||
ssh_channel_request_pty
|
||||
ssh_channel_request_pty_size
|
||||
ssh_channel_request_pty_size_modes
|
||||
ssh_channel_request_send_break
|
||||
ssh_channel_request_send_exit_signal
|
||||
ssh_channel_request_send_exit_status
|
||||
ssh_channel_request_send_signal
|
||||
ssh_channel_request_sftp
|
||||
ssh_channel_request_shell
|
||||
ssh_channel_request_subsystem
|
||||
ssh_channel_request_x11
|
||||
ssh_channel_select
|
||||
ssh_channel_send_eof
|
||||
ssh_channel_set_blocking
|
||||
ssh_channel_set_counter
|
||||
ssh_channel_window_size
|
||||
ssh_channel_write
|
||||
ssh_channel_write_stderr
|
||||
ssh_clean_pubkey_hash
|
||||
ssh_connect
|
||||
ssh_connector_free
|
||||
ssh_connector_new
|
||||
ssh_connector_set_in_channel
|
||||
ssh_connector_set_in_fd
|
||||
ssh_connector_set_out_channel
|
||||
ssh_connector_set_out_fd
|
||||
ssh_copyright
|
||||
ssh_dirname
|
||||
ssh_disconnect
|
||||
ssh_dump_knownhost
|
||||
ssh_event_add_connector
|
||||
ssh_event_add_fd
|
||||
ssh_event_add_session
|
||||
ssh_event_dopoll
|
||||
ssh_event_free
|
||||
ssh_event_new
|
||||
ssh_event_remove_connector
|
||||
ssh_event_remove_fd
|
||||
ssh_event_remove_session
|
||||
ssh_execute_message_callbacks
|
||||
ssh_finalize
|
||||
ssh_forward_accept
|
||||
ssh_forward_cancel
|
||||
ssh_forward_listen
|
||||
ssh_free
|
||||
ssh_get_cipher_in
|
||||
ssh_get_cipher_out
|
||||
ssh_get_clientbanner
|
||||
ssh_get_disconnect_message
|
||||
ssh_get_error
|
||||
ssh_get_error_code
|
||||
ssh_get_fd
|
||||
ssh_get_fingerprint_hash
|
||||
ssh_get_hexa
|
||||
ssh_get_hmac_in
|
||||
ssh_get_hmac_out
|
||||
ssh_get_issue_banner
|
||||
ssh_get_kex_algo
|
||||
ssh_get_log_callback
|
||||
ssh_get_log_level
|
||||
ssh_get_log_userdata
|
||||
ssh_get_openssh_version
|
||||
ssh_get_poll_flags
|
||||
ssh_get_pubkey
|
||||
ssh_get_pubkey_hash
|
||||
ssh_get_publickey
|
||||
ssh_get_publickey_hash
|
||||
ssh_get_random
|
||||
ssh_get_server_publickey
|
||||
ssh_get_serverbanner
|
||||
ssh_get_status
|
||||
ssh_get_version
|
||||
ssh_getpass
|
||||
ssh_gssapi_get_creds
|
||||
ssh_gssapi_set_creds
|
||||
ssh_handle_key_exchange
|
||||
ssh_init
|
||||
ssh_is_blocking
|
||||
ssh_is_connected
|
||||
ssh_is_server_known
|
||||
ssh_key_cmp
|
||||
ssh_key_dup
|
||||
ssh_key_free
|
||||
ssh_key_is_private
|
||||
ssh_key_is_public
|
||||
ssh_key_new
|
||||
ssh_key_type
|
||||
ssh_key_type_from_name
|
||||
ssh_key_type_to_char
|
||||
ssh_known_hosts_parse_line
|
||||
ssh_knownhosts_entry_free
|
||||
ssh_log
|
||||
ssh_message_auth_interactive_request
|
||||
ssh_message_auth_kbdint_is_response
|
||||
ssh_message_auth_password
|
||||
ssh_message_auth_pubkey
|
||||
ssh_message_auth_publickey
|
||||
ssh_message_auth_publickey_state
|
||||
ssh_message_auth_reply_pk_ok
|
||||
ssh_message_auth_reply_pk_ok_simple
|
||||
ssh_message_auth_reply_success
|
||||
ssh_message_auth_set_methods
|
||||
ssh_message_auth_user
|
||||
ssh_message_channel_request_channel
|
||||
ssh_message_channel_request_command
|
||||
ssh_message_channel_request_env_name
|
||||
ssh_message_channel_request_env_value
|
||||
ssh_message_channel_request_open_destination
|
||||
ssh_message_channel_request_open_destination_port
|
||||
ssh_message_channel_request_open_originator
|
||||
ssh_message_channel_request_open_originator_port
|
||||
ssh_message_channel_request_open_reply_accept
|
||||
ssh_message_channel_request_open_reply_accept_channel
|
||||
ssh_message_channel_request_pty_height
|
||||
ssh_message_channel_request_pty_pxheight
|
||||
ssh_message_channel_request_pty_pxwidth
|
||||
ssh_message_channel_request_pty_term
|
||||
ssh_message_channel_request_pty_width
|
||||
ssh_message_channel_request_reply_success
|
||||
ssh_message_channel_request_subsystem
|
||||
ssh_message_channel_request_x11_auth_cookie
|
||||
ssh_message_channel_request_x11_auth_protocol
|
||||
ssh_message_channel_request_x11_screen_number
|
||||
ssh_message_channel_request_x11_single_connection
|
||||
ssh_message_free
|
||||
ssh_message_get
|
||||
ssh_message_global_request_address
|
||||
ssh_message_global_request_port
|
||||
ssh_message_global_request_reply_success
|
||||
ssh_message_reply_default
|
||||
ssh_message_retrieve
|
||||
ssh_message_service_reply_success
|
||||
ssh_message_service_service
|
||||
ssh_message_subtype
|
||||
ssh_message_type
|
||||
ssh_mkdir
|
||||
ssh_new
|
||||
ssh_options_copy
|
||||
ssh_options_get
|
||||
ssh_options_get_port
|
||||
ssh_options_getopt
|
||||
ssh_options_parse_config
|
||||
ssh_options_set
|
||||
ssh_pcap_file_close
|
||||
ssh_pcap_file_free
|
||||
ssh_pcap_file_new
|
||||
ssh_pcap_file_open
|
||||
ssh_pki_copy_cert_to_privkey
|
||||
ssh_pki_export_privkey_base64
|
||||
ssh_pki_export_privkey_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
ssh_pki_export_privkey_to_pubkey
|
||||
ssh_pki_export_pubkey_base64
|
||||
ssh_pki_export_pubkey_file
|
||||
ssh_pki_generate
|
||||
ssh_pki_import_cert_base64
|
||||
ssh_pki_import_cert_file
|
||||
ssh_pki_import_privkey_base64
|
||||
ssh_pki_import_privkey_file
|
||||
ssh_pki_import_pubkey_base64
|
||||
ssh_pki_import_pubkey_file
|
||||
ssh_pki_key_ecdsa_name
|
||||
ssh_print_hash
|
||||
ssh_print_hexa
|
||||
ssh_privatekey_type
|
||||
ssh_publickey_to_file
|
||||
ssh_remove_channel_callbacks
|
||||
ssh_request_no_more_sessions
|
||||
ssh_scp_accept_request
|
||||
ssh_scp_close
|
||||
ssh_scp_deny_request
|
||||
ssh_scp_free
|
||||
ssh_scp_init
|
||||
ssh_scp_leave_directory
|
||||
ssh_scp_new
|
||||
ssh_scp_pull_request
|
||||
ssh_scp_push_directory
|
||||
ssh_scp_push_file
|
||||
ssh_scp_push_file64
|
||||
ssh_scp_read
|
||||
ssh_scp_request_get_filename
|
||||
ssh_scp_request_get_permissions
|
||||
ssh_scp_request_get_size
|
||||
ssh_scp_request_get_size64
|
||||
ssh_scp_request_get_warning
|
||||
ssh_scp_write
|
||||
ssh_select
|
||||
ssh_send_debug
|
||||
ssh_send_ignore
|
||||
ssh_send_issue_banner
|
||||
ssh_send_keepalive
|
||||
ssh_server_init_kex
|
||||
ssh_service_request
|
||||
ssh_session_export_known_hosts_entry
|
||||
ssh_session_get_known_hosts_entry
|
||||
ssh_session_has_known_hosts_entry
|
||||
ssh_session_is_known_server
|
||||
ssh_session_set_disconnect_message
|
||||
ssh_session_update_known_hosts
|
||||
ssh_set_agent_channel
|
||||
ssh_set_agent_socket
|
||||
ssh_set_auth_methods
|
||||
ssh_set_blocking
|
||||
ssh_set_callbacks
|
||||
ssh_set_channel_callbacks
|
||||
ssh_set_counters
|
||||
ssh_set_fd_except
|
||||
ssh_set_fd_toread
|
||||
ssh_set_fd_towrite
|
||||
ssh_set_log_callback
|
||||
ssh_set_log_level
|
||||
ssh_set_log_userdata
|
||||
ssh_set_message_callback
|
||||
ssh_set_pcap_file
|
||||
ssh_set_server_callbacks
|
||||
ssh_silent_disconnect
|
||||
ssh_string_burn
|
||||
ssh_string_copy
|
||||
ssh_string_data
|
||||
ssh_string_fill
|
||||
ssh_string_free
|
||||
ssh_string_free_char
|
||||
ssh_string_from_char
|
||||
ssh_string_get_char
|
||||
ssh_string_len
|
||||
ssh_string_new
|
||||
ssh_string_to_char
|
||||
ssh_threads_get_default
|
||||
ssh_threads_get_noop
|
||||
ssh_threads_get_pthread
|
||||
ssh_threads_set_callbacks
|
||||
ssh_try_publickey_from_file
|
||||
ssh_userauth_agent
|
||||
ssh_userauth_agent_pubkey
|
||||
ssh_userauth_autopubkey
|
||||
ssh_userauth_gssapi
|
||||
ssh_userauth_kbdint
|
||||
ssh_userauth_kbdint_getanswer
|
||||
ssh_userauth_kbdint_getinstruction
|
||||
ssh_userauth_kbdint_getname
|
||||
ssh_userauth_kbdint_getnanswers
|
||||
ssh_userauth_kbdint_getnprompts
|
||||
ssh_userauth_kbdint_getprompt
|
||||
ssh_userauth_kbdint_setanswer
|
||||
ssh_userauth_list
|
||||
ssh_userauth_none
|
||||
ssh_userauth_offer_pubkey
|
||||
ssh_userauth_password
|
||||
ssh_userauth_privatekey_file
|
||||
ssh_userauth_pubkey
|
||||
ssh_userauth_publickey
|
||||
ssh_userauth_publickey_auto
|
||||
ssh_userauth_publickey_auto_get_current_identity
|
||||
ssh_userauth_try_publickey
|
||||
ssh_version
|
||||
ssh_vlog
|
||||
ssh_write_knownhost
|
||||
string_burn
|
||||
string_copy
|
||||
string_data
|
||||
string_fill
|
||||
string_free
|
||||
string_from_char
|
||||
string_len
|
||||
string_new
|
||||
string_to_char
|
||||
445
src/ABI/libssh-4.10.4.symbols
Normal file
445
src/ABI/libssh-4.10.4.symbols
Normal file
@@ -0,0 +1,445 @@
|
||||
_ssh_log
|
||||
buffer_free
|
||||
buffer_get
|
||||
buffer_get_len
|
||||
buffer_new
|
||||
channel_accept_x11
|
||||
channel_change_pty_size
|
||||
channel_close
|
||||
channel_forward_accept
|
||||
channel_forward_cancel
|
||||
channel_forward_listen
|
||||
channel_free
|
||||
channel_get_exit_status
|
||||
channel_get_session
|
||||
channel_is_closed
|
||||
channel_is_eof
|
||||
channel_is_open
|
||||
channel_new
|
||||
channel_open_forward
|
||||
channel_open_session
|
||||
channel_poll
|
||||
channel_read
|
||||
channel_read_buffer
|
||||
channel_read_nonblocking
|
||||
channel_request_env
|
||||
channel_request_exec
|
||||
channel_request_pty
|
||||
channel_request_pty_size
|
||||
channel_request_send_signal
|
||||
channel_request_sftp
|
||||
channel_request_shell
|
||||
channel_request_subsystem
|
||||
channel_request_x11
|
||||
channel_select
|
||||
channel_send_eof
|
||||
channel_set_blocking
|
||||
channel_write
|
||||
channel_write_stderr
|
||||
privatekey_free
|
||||
privatekey_from_file
|
||||
publickey_free
|
||||
publickey_from_file
|
||||
publickey_from_privatekey
|
||||
publickey_to_string
|
||||
sftp_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
sftp_chmod
|
||||
sftp_chown
|
||||
sftp_client_message_free
|
||||
sftp_client_message_get_data
|
||||
sftp_client_message_get_filename
|
||||
sftp_client_message_get_flags
|
||||
sftp_client_message_get_submessage
|
||||
sftp_client_message_get_type
|
||||
sftp_client_message_set_filename
|
||||
sftp_close
|
||||
sftp_closedir
|
||||
sftp_dir_eof
|
||||
sftp_expand_path
|
||||
sftp_extension_supported
|
||||
sftp_extensions_get_count
|
||||
sftp_extensions_get_data
|
||||
sftp_extensions_get_name
|
||||
sftp_file_set_blocking
|
||||
sftp_file_set_nonblocking
|
||||
sftp_free
|
||||
sftp_fstat
|
||||
sftp_fstatvfs
|
||||
sftp_fsync
|
||||
sftp_get_client_message
|
||||
sftp_get_error
|
||||
sftp_handle
|
||||
sftp_handle_alloc
|
||||
sftp_handle_remove
|
||||
sftp_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
sftp_lstat
|
||||
sftp_mkdir
|
||||
sftp_new
|
||||
sftp_new_channel
|
||||
sftp_open
|
||||
sftp_opendir
|
||||
sftp_read
|
||||
sftp_readdir
|
||||
sftp_readlink
|
||||
sftp_rename
|
||||
sftp_reply_attr
|
||||
sftp_reply_data
|
||||
sftp_reply_handle
|
||||
sftp_reply_name
|
||||
sftp_reply_names
|
||||
sftp_reply_names_add
|
||||
sftp_reply_status
|
||||
sftp_rewind
|
||||
sftp_rmdir
|
||||
sftp_seek
|
||||
sftp_seek64
|
||||
sftp_send_client_message
|
||||
sftp_server_free
|
||||
sftp_server_init
|
||||
sftp_server_new
|
||||
sftp_server_version
|
||||
sftp_setstat
|
||||
sftp_stat
|
||||
sftp_statvfs
|
||||
sftp_statvfs_free
|
||||
sftp_symlink
|
||||
sftp_tell
|
||||
sftp_tell64
|
||||
sftp_unlink
|
||||
sftp_utimes
|
||||
sftp_write
|
||||
ssh_accept
|
||||
ssh_add_channel_callbacks
|
||||
ssh_auth_list
|
||||
ssh_basename
|
||||
ssh_bind_accept
|
||||
ssh_bind_accept_fd
|
||||
ssh_bind_fd_toaccept
|
||||
ssh_bind_free
|
||||
ssh_bind_get_fd
|
||||
ssh_bind_listen
|
||||
ssh_bind_new
|
||||
ssh_bind_options_parse_config
|
||||
ssh_bind_options_set
|
||||
ssh_bind_set_blocking
|
||||
ssh_bind_set_callbacks
|
||||
ssh_bind_set_fd
|
||||
ssh_blocking_flush
|
||||
ssh_buffer_add_data
|
||||
ssh_buffer_free
|
||||
ssh_buffer_get
|
||||
ssh_buffer_get_data
|
||||
ssh_buffer_get_len
|
||||
ssh_buffer_new
|
||||
ssh_buffer_reinit
|
||||
ssh_channel_accept_forward
|
||||
ssh_channel_accept_x11
|
||||
ssh_channel_cancel_forward
|
||||
ssh_channel_change_pty_size
|
||||
ssh_channel_close
|
||||
ssh_channel_free
|
||||
ssh_channel_get_exit_state
|
||||
ssh_channel_get_exit_status
|
||||
ssh_channel_get_session
|
||||
ssh_channel_is_closed
|
||||
ssh_channel_is_eof
|
||||
ssh_channel_is_open
|
||||
ssh_channel_listen_forward
|
||||
ssh_channel_new
|
||||
ssh_channel_open_auth_agent
|
||||
ssh_channel_open_forward
|
||||
ssh_channel_open_forward_port
|
||||
ssh_channel_open_forward_unix
|
||||
ssh_channel_open_reverse_forward
|
||||
ssh_channel_open_session
|
||||
ssh_channel_open_x11
|
||||
ssh_channel_poll
|
||||
ssh_channel_poll_timeout
|
||||
ssh_channel_read
|
||||
ssh_channel_read_nonblocking
|
||||
ssh_channel_read_timeout
|
||||
ssh_channel_request_auth_agent
|
||||
ssh_channel_request_env
|
||||
ssh_channel_request_exec
|
||||
ssh_channel_request_pty
|
||||
ssh_channel_request_pty_size
|
||||
ssh_channel_request_pty_size_modes
|
||||
ssh_channel_request_send_break
|
||||
ssh_channel_request_send_exit_signal
|
||||
ssh_channel_request_send_exit_status
|
||||
ssh_channel_request_send_signal
|
||||
ssh_channel_request_sftp
|
||||
ssh_channel_request_shell
|
||||
ssh_channel_request_subsystem
|
||||
ssh_channel_request_x11
|
||||
ssh_channel_select
|
||||
ssh_channel_send_eof
|
||||
ssh_channel_set_blocking
|
||||
ssh_channel_set_counter
|
||||
ssh_channel_window_size
|
||||
ssh_channel_write
|
||||
ssh_channel_write_stderr
|
||||
ssh_clean_pubkey_hash
|
||||
ssh_connect
|
||||
ssh_connector_free
|
||||
ssh_connector_new
|
||||
ssh_connector_set_in_channel
|
||||
ssh_connector_set_in_fd
|
||||
ssh_connector_set_out_channel
|
||||
ssh_connector_set_out_fd
|
||||
ssh_copyright
|
||||
ssh_dirname
|
||||
ssh_disconnect
|
||||
ssh_dump_knownhost
|
||||
ssh_event_add_connector
|
||||
ssh_event_add_fd
|
||||
ssh_event_add_session
|
||||
ssh_event_dopoll
|
||||
ssh_event_free
|
||||
ssh_event_new
|
||||
ssh_event_remove_connector
|
||||
ssh_event_remove_fd
|
||||
ssh_event_remove_session
|
||||
ssh_execute_message_callbacks
|
||||
ssh_finalize
|
||||
ssh_forward_accept
|
||||
ssh_forward_cancel
|
||||
ssh_forward_listen
|
||||
ssh_free
|
||||
ssh_get_cipher_in
|
||||
ssh_get_cipher_out
|
||||
ssh_get_clientbanner
|
||||
ssh_get_disconnect_message
|
||||
ssh_get_error
|
||||
ssh_get_error_code
|
||||
ssh_get_fd
|
||||
ssh_get_fingerprint_hash
|
||||
ssh_get_hexa
|
||||
ssh_get_hmac_in
|
||||
ssh_get_hmac_out
|
||||
ssh_get_issue_banner
|
||||
ssh_get_kex_algo
|
||||
ssh_get_log_callback
|
||||
ssh_get_log_level
|
||||
ssh_get_log_userdata
|
||||
ssh_get_openssh_version
|
||||
ssh_get_poll_flags
|
||||
ssh_get_pubkey
|
||||
ssh_get_pubkey_hash
|
||||
ssh_get_publickey
|
||||
ssh_get_publickey_hash
|
||||
ssh_get_random
|
||||
ssh_get_server_publickey
|
||||
ssh_get_serverbanner
|
||||
ssh_get_status
|
||||
ssh_get_version
|
||||
ssh_getpass
|
||||
ssh_gssapi_get_creds
|
||||
ssh_gssapi_set_creds
|
||||
ssh_handle_key_exchange
|
||||
ssh_init
|
||||
ssh_is_blocking
|
||||
ssh_is_connected
|
||||
ssh_is_server_known
|
||||
ssh_key_cmp
|
||||
ssh_key_dup
|
||||
ssh_key_free
|
||||
ssh_key_is_private
|
||||
ssh_key_is_public
|
||||
ssh_key_new
|
||||
ssh_key_type
|
||||
ssh_key_type_from_name
|
||||
ssh_key_type_to_char
|
||||
ssh_known_hosts_parse_line
|
||||
ssh_knownhosts_entry_free
|
||||
ssh_log
|
||||
ssh_message_auth_interactive_request
|
||||
ssh_message_auth_kbdint_is_response
|
||||
ssh_message_auth_password
|
||||
ssh_message_auth_pubkey
|
||||
ssh_message_auth_publickey
|
||||
ssh_message_auth_publickey_state
|
||||
ssh_message_auth_reply_pk_ok
|
||||
ssh_message_auth_reply_pk_ok_simple
|
||||
ssh_message_auth_reply_success
|
||||
ssh_message_auth_set_methods
|
||||
ssh_message_auth_user
|
||||
ssh_message_channel_request_channel
|
||||
ssh_message_channel_request_command
|
||||
ssh_message_channel_request_env_name
|
||||
ssh_message_channel_request_env_value
|
||||
ssh_message_channel_request_open_destination
|
||||
ssh_message_channel_request_open_destination_port
|
||||
ssh_message_channel_request_open_originator
|
||||
ssh_message_channel_request_open_originator_port
|
||||
ssh_message_channel_request_open_reply_accept
|
||||
ssh_message_channel_request_open_reply_accept_channel
|
||||
ssh_message_channel_request_pty_height
|
||||
ssh_message_channel_request_pty_pxheight
|
||||
ssh_message_channel_request_pty_pxwidth
|
||||
ssh_message_channel_request_pty_term
|
||||
ssh_message_channel_request_pty_width
|
||||
ssh_message_channel_request_reply_success
|
||||
ssh_message_channel_request_subsystem
|
||||
ssh_message_channel_request_x11_auth_cookie
|
||||
ssh_message_channel_request_x11_auth_protocol
|
||||
ssh_message_channel_request_x11_screen_number
|
||||
ssh_message_channel_request_x11_single_connection
|
||||
ssh_message_free
|
||||
ssh_message_get
|
||||
ssh_message_global_request_address
|
||||
ssh_message_global_request_port
|
||||
ssh_message_global_request_reply_success
|
||||
ssh_message_reply_default
|
||||
ssh_message_retrieve
|
||||
ssh_message_service_reply_success
|
||||
ssh_message_service_service
|
||||
ssh_message_subtype
|
||||
ssh_message_type
|
||||
ssh_mkdir
|
||||
ssh_new
|
||||
ssh_options_copy
|
||||
ssh_options_get
|
||||
ssh_options_get_port
|
||||
ssh_options_getopt
|
||||
ssh_options_parse_config
|
||||
ssh_options_set
|
||||
ssh_pcap_file_close
|
||||
ssh_pcap_file_free
|
||||
ssh_pcap_file_new
|
||||
ssh_pcap_file_open
|
||||
ssh_pki_copy_cert_to_privkey
|
||||
ssh_pki_export_privkey_base64
|
||||
ssh_pki_export_privkey_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
ssh_pki_export_privkey_to_pubkey
|
||||
ssh_pki_export_pubkey_base64
|
||||
ssh_pki_export_pubkey_file
|
||||
ssh_pki_generate
|
||||
ssh_pki_import_cert_base64
|
||||
ssh_pki_import_cert_file
|
||||
ssh_pki_import_privkey_base64
|
||||
ssh_pki_import_privkey_file
|
||||
ssh_pki_import_pubkey_base64
|
||||
ssh_pki_import_pubkey_file
|
||||
ssh_pki_key_ecdsa_name
|
||||
ssh_print_hash
|
||||
ssh_print_hexa
|
||||
ssh_privatekey_type
|
||||
ssh_publickey_to_file
|
||||
ssh_remove_channel_callbacks
|
||||
ssh_request_no_more_sessions
|
||||
ssh_scp_accept_request
|
||||
ssh_scp_close
|
||||
ssh_scp_deny_request
|
||||
ssh_scp_free
|
||||
ssh_scp_init
|
||||
ssh_scp_leave_directory
|
||||
ssh_scp_new
|
||||
ssh_scp_pull_request
|
||||
ssh_scp_push_directory
|
||||
ssh_scp_push_file
|
||||
ssh_scp_push_file64
|
||||
ssh_scp_read
|
||||
ssh_scp_request_get_filename
|
||||
ssh_scp_request_get_permissions
|
||||
ssh_scp_request_get_size
|
||||
ssh_scp_request_get_size64
|
||||
ssh_scp_request_get_warning
|
||||
ssh_scp_write
|
||||
ssh_select
|
||||
ssh_send_debug
|
||||
ssh_send_ignore
|
||||
ssh_send_issue_banner
|
||||
ssh_send_keepalive
|
||||
ssh_server_init_kex
|
||||
ssh_service_request
|
||||
ssh_session_export_known_hosts_entry
|
||||
ssh_session_get_known_hosts_entry
|
||||
ssh_session_has_known_hosts_entry
|
||||
ssh_session_is_known_server
|
||||
ssh_session_set_disconnect_message
|
||||
ssh_session_update_known_hosts
|
||||
ssh_set_agent_channel
|
||||
ssh_set_agent_socket
|
||||
ssh_set_auth_methods
|
||||
ssh_set_blocking
|
||||
ssh_set_callbacks
|
||||
ssh_set_channel_callbacks
|
||||
ssh_set_counters
|
||||
ssh_set_fd_except
|
||||
ssh_set_fd_toread
|
||||
ssh_set_fd_towrite
|
||||
ssh_set_log_callback
|
||||
ssh_set_log_level
|
||||
ssh_set_log_userdata
|
||||
ssh_set_message_callback
|
||||
ssh_set_pcap_file
|
||||
ssh_set_server_callbacks
|
||||
ssh_silent_disconnect
|
||||
ssh_string_burn
|
||||
ssh_string_copy
|
||||
ssh_string_data
|
||||
ssh_string_fill
|
||||
ssh_string_free
|
||||
ssh_string_free_char
|
||||
ssh_string_from_char
|
||||
ssh_string_get_char
|
||||
ssh_string_len
|
||||
ssh_string_new
|
||||
ssh_string_to_char
|
||||
ssh_threads_get_default
|
||||
ssh_threads_get_noop
|
||||
ssh_threads_get_pthread
|
||||
ssh_threads_set_callbacks
|
||||
ssh_try_publickey_from_file
|
||||
ssh_userauth_agent
|
||||
ssh_userauth_agent_pubkey
|
||||
ssh_userauth_autopubkey
|
||||
ssh_userauth_gssapi
|
||||
ssh_userauth_kbdint
|
||||
ssh_userauth_kbdint_getanswer
|
||||
ssh_userauth_kbdint_getinstruction
|
||||
ssh_userauth_kbdint_getname
|
||||
ssh_userauth_kbdint_getnanswers
|
||||
ssh_userauth_kbdint_getnprompts
|
||||
ssh_userauth_kbdint_getprompt
|
||||
ssh_userauth_kbdint_setanswer
|
||||
ssh_userauth_list
|
||||
ssh_userauth_none
|
||||
ssh_userauth_offer_pubkey
|
||||
ssh_userauth_password
|
||||
ssh_userauth_privatekey_file
|
||||
ssh_userauth_pubkey
|
||||
ssh_userauth_publickey
|
||||
ssh_userauth_publickey_auto
|
||||
ssh_userauth_publickey_auto_get_current_identity
|
||||
ssh_userauth_try_publickey
|
||||
ssh_version
|
||||
ssh_vlog
|
||||
ssh_write_knownhost
|
||||
string_burn
|
||||
string_copy
|
||||
string_data
|
||||
string_fill
|
||||
string_free
|
||||
string_from_char
|
||||
string_len
|
||||
string_new
|
||||
string_to_char
|
||||
465
src/ABI/libssh-4.11.0.symbols
Normal file
465
src/ABI/libssh-4.11.0.symbols
Normal file
@@ -0,0 +1,465 @@
|
||||
_ssh_log
|
||||
buffer_free
|
||||
buffer_get
|
||||
buffer_get_len
|
||||
buffer_new
|
||||
channel_accept_x11
|
||||
channel_change_pty_size
|
||||
channel_close
|
||||
channel_forward_accept
|
||||
channel_forward_cancel
|
||||
channel_forward_listen
|
||||
channel_free
|
||||
channel_get_exit_status
|
||||
channel_get_session
|
||||
channel_is_closed
|
||||
channel_is_eof
|
||||
channel_is_open
|
||||
channel_new
|
||||
channel_open_forward
|
||||
channel_open_session
|
||||
channel_poll
|
||||
channel_read
|
||||
channel_read_buffer
|
||||
channel_read_nonblocking
|
||||
channel_request_env
|
||||
channel_request_exec
|
||||
channel_request_pty
|
||||
channel_request_pty_size
|
||||
channel_request_send_signal
|
||||
channel_request_sftp
|
||||
channel_request_shell
|
||||
channel_request_subsystem
|
||||
channel_request_x11
|
||||
channel_select
|
||||
channel_send_eof
|
||||
channel_set_blocking
|
||||
channel_write
|
||||
channel_write_stderr
|
||||
privatekey_free
|
||||
privatekey_from_file
|
||||
publickey_free
|
||||
publickey_from_file
|
||||
publickey_from_privatekey
|
||||
publickey_to_string
|
||||
sftp_aio_begin_read
|
||||
sftp_aio_begin_write
|
||||
sftp_aio_free
|
||||
sftp_aio_wait_read
|
||||
sftp_aio_wait_write
|
||||
sftp_async_read
|
||||
sftp_async_read_begin
|
||||
sftp_attributes_free
|
||||
sftp_canonicalize_path
|
||||
sftp_channel_default_data_callback
|
||||
sftp_channel_default_subsystem_request
|
||||
sftp_chmod
|
||||
sftp_chown
|
||||
sftp_client_message_free
|
||||
sftp_client_message_get_data
|
||||
sftp_client_message_get_filename
|
||||
sftp_client_message_get_flags
|
||||
sftp_client_message_get_submessage
|
||||
sftp_client_message_get_type
|
||||
sftp_client_message_set_filename
|
||||
sftp_close
|
||||
sftp_closedir
|
||||
sftp_dir_eof
|
||||
sftp_expand_path
|
||||
sftp_extension_supported
|
||||
sftp_extensions_get_count
|
||||
sftp_extensions_get_data
|
||||
sftp_extensions_get_name
|
||||
sftp_file_set_blocking
|
||||
sftp_file_set_nonblocking
|
||||
sftp_free
|
||||
sftp_fstat
|
||||
sftp_fstatvfs
|
||||
sftp_fsync
|
||||
sftp_get_client_message
|
||||
sftp_get_error
|
||||
sftp_get_users_groups_by_id
|
||||
sftp_handle
|
||||
sftp_handle_alloc
|
||||
sftp_handle_remove
|
||||
sftp_hardlink
|
||||
sftp_home_directory
|
||||
sftp_init
|
||||
sftp_limits
|
||||
sftp_limits_free
|
||||
sftp_lsetstat
|
||||
sftp_lstat
|
||||
sftp_mkdir
|
||||
sftp_name_id_map_free
|
||||
sftp_name_id_map_new
|
||||
sftp_new
|
||||
sftp_new_channel
|
||||
sftp_open
|
||||
sftp_opendir
|
||||
sftp_read
|
||||
sftp_readdir
|
||||
sftp_readlink
|
||||
sftp_rename
|
||||
sftp_reply_attr
|
||||
sftp_reply_data
|
||||
sftp_reply_handle
|
||||
sftp_reply_name
|
||||
sftp_reply_names
|
||||
sftp_reply_names_add
|
||||
sftp_reply_status
|
||||
sftp_rewind
|
||||
sftp_rmdir
|
||||
sftp_seek
|
||||
sftp_seek64
|
||||
sftp_send_client_message
|
||||
sftp_server_free
|
||||
sftp_server_init
|
||||
sftp_server_new
|
||||
sftp_server_version
|
||||
sftp_setstat
|
||||
sftp_stat
|
||||
sftp_statvfs
|
||||
sftp_statvfs_free
|
||||
sftp_symlink
|
||||
sftp_tell
|
||||
sftp_tell64
|
||||
sftp_unlink
|
||||
sftp_utimes
|
||||
sftp_write
|
||||
ssh_accept
|
||||
ssh_add_channel_callbacks
|
||||
ssh_auth_list
|
||||
ssh_basename
|
||||
ssh_bind_accept
|
||||
ssh_bind_accept_fd
|
||||
ssh_bind_fd_toaccept
|
||||
ssh_bind_free
|
||||
ssh_bind_get_fd
|
||||
ssh_bind_listen
|
||||
ssh_bind_new
|
||||
ssh_bind_options_parse_config
|
||||
ssh_bind_options_set
|
||||
ssh_bind_set_blocking
|
||||
ssh_bind_set_callbacks
|
||||
ssh_bind_set_fd
|
||||
ssh_blocking_flush
|
||||
ssh_buffer_add_data
|
||||
ssh_buffer_free
|
||||
ssh_buffer_get
|
||||
ssh_buffer_get_data
|
||||
ssh_buffer_get_len
|
||||
ssh_buffer_new
|
||||
ssh_buffer_reinit
|
||||
ssh_channel_accept_forward
|
||||
ssh_channel_accept_x11
|
||||
ssh_channel_cancel_forward
|
||||
ssh_channel_change_pty_size
|
||||
ssh_channel_close
|
||||
ssh_channel_free
|
||||
ssh_channel_get_exit_state
|
||||
ssh_channel_get_exit_status
|
||||
ssh_channel_get_session
|
||||
ssh_channel_is_closed
|
||||
ssh_channel_is_eof
|
||||
ssh_channel_is_open
|
||||
ssh_channel_listen_forward
|
||||
ssh_channel_new
|
||||
ssh_channel_open_auth_agent
|
||||
ssh_channel_open_forward
|
||||
ssh_channel_open_forward_port
|
||||
ssh_channel_open_forward_unix
|
||||
ssh_channel_open_reverse_forward
|
||||
ssh_channel_open_session
|
||||
ssh_channel_open_x11
|
||||
ssh_channel_poll
|
||||
ssh_channel_poll_timeout
|
||||
ssh_channel_read
|
||||
ssh_channel_read_nonblocking
|
||||
ssh_channel_read_timeout
|
||||
ssh_channel_request_auth_agent
|
||||
ssh_channel_request_env
|
||||
ssh_channel_request_exec
|
||||
ssh_channel_request_pty
|
||||
ssh_channel_request_pty_size
|
||||
ssh_channel_request_pty_size_modes
|
||||
ssh_channel_request_send_break
|
||||
ssh_channel_request_send_exit_signal
|
||||
ssh_channel_request_send_exit_status
|
||||
ssh_channel_request_send_signal
|
||||
ssh_channel_request_sftp
|
||||
ssh_channel_request_shell
|
||||
ssh_channel_request_subsystem
|
||||
ssh_channel_request_x11
|
||||
ssh_channel_select
|
||||
ssh_channel_send_eof
|
||||
ssh_channel_set_blocking
|
||||
ssh_channel_set_counter
|
||||
ssh_channel_window_size
|
||||
ssh_channel_write
|
||||
ssh_channel_write_stderr
|
||||
ssh_clean_pubkey_hash
|
||||
ssh_connect
|
||||
ssh_connector_free
|
||||
ssh_connector_new
|
||||
ssh_connector_set_in_channel
|
||||
ssh_connector_set_in_fd
|
||||
ssh_connector_set_out_channel
|
||||
ssh_connector_set_out_fd
|
||||
ssh_copyright
|
||||
ssh_dirname
|
||||
ssh_disconnect
|
||||
ssh_dump_knownhost
|
||||
ssh_event_add_connector
|
||||
ssh_event_add_fd
|
||||
ssh_event_add_session
|
||||
ssh_event_dopoll
|
||||
ssh_event_free
|
||||
ssh_event_new
|
||||
ssh_event_remove_connector
|
||||
ssh_event_remove_fd
|
||||
ssh_event_remove_session
|
||||
ssh_execute_message_callbacks
|
||||
ssh_finalize
|
||||
ssh_forward_accept
|
||||
ssh_forward_cancel
|
||||
ssh_forward_listen
|
||||
ssh_free
|
||||
ssh_get_cipher_in
|
||||
ssh_get_cipher_out
|
||||
ssh_get_clientbanner
|
||||
ssh_get_disconnect_message
|
||||
ssh_get_error
|
||||
ssh_get_error_code
|
||||
ssh_get_fd
|
||||
ssh_get_fingerprint_hash
|
||||
ssh_get_hexa
|
||||
ssh_get_hmac_in
|
||||
ssh_get_hmac_out
|
||||
ssh_get_issue_banner
|
||||
ssh_get_kex_algo
|
||||
ssh_get_log_callback
|
||||
ssh_get_log_level
|
||||
ssh_get_log_userdata
|
||||
ssh_get_openssh_version
|
||||
ssh_get_poll_flags
|
||||
ssh_get_pubkey
|
||||
ssh_get_pubkey_hash
|
||||
ssh_get_publickey
|
||||
ssh_get_publickey_hash
|
||||
ssh_get_random
|
||||
ssh_get_server_publickey
|
||||
ssh_get_serverbanner
|
||||
ssh_get_status
|
||||
ssh_get_supported_methods
|
||||
ssh_get_version
|
||||
ssh_getpass
|
||||
ssh_gssapi_get_creds
|
||||
ssh_gssapi_set_creds
|
||||
ssh_handle_key_exchange
|
||||
ssh_init
|
||||
ssh_is_blocking
|
||||
ssh_is_connected
|
||||
ssh_is_server_known
|
||||
ssh_key_cmp
|
||||
ssh_key_dup
|
||||
ssh_key_free
|
||||
ssh_key_get_sk_application
|
||||
ssh_key_get_sk_flags
|
||||
ssh_key_get_sk_user_id
|
||||
ssh_key_is_private
|
||||
ssh_key_is_public
|
||||
ssh_key_new
|
||||
ssh_key_type
|
||||
ssh_key_type_from_name
|
||||
ssh_key_type_to_char
|
||||
ssh_known_hosts_parse_line
|
||||
ssh_knownhosts_entry_free
|
||||
ssh_log
|
||||
ssh_message_auth_interactive_request
|
||||
ssh_message_auth_kbdint_is_response
|
||||
ssh_message_auth_password
|
||||
ssh_message_auth_pubkey
|
||||
ssh_message_auth_publickey
|
||||
ssh_message_auth_publickey_state
|
||||
ssh_message_auth_reply_pk_ok
|
||||
ssh_message_auth_reply_pk_ok_simple
|
||||
ssh_message_auth_reply_success
|
||||
ssh_message_auth_set_methods
|
||||
ssh_message_auth_user
|
||||
ssh_message_channel_request_channel
|
||||
ssh_message_channel_request_command
|
||||
ssh_message_channel_request_env_name
|
||||
ssh_message_channel_request_env_value
|
||||
ssh_message_channel_request_open_destination
|
||||
ssh_message_channel_request_open_destination_port
|
||||
ssh_message_channel_request_open_originator
|
||||
ssh_message_channel_request_open_originator_port
|
||||
ssh_message_channel_request_open_reply_accept
|
||||
ssh_message_channel_request_open_reply_accept_channel
|
||||
ssh_message_channel_request_pty_height
|
||||
ssh_message_channel_request_pty_pxheight
|
||||
ssh_message_channel_request_pty_pxwidth
|
||||
ssh_message_channel_request_pty_term
|
||||
ssh_message_channel_request_pty_width
|
||||
ssh_message_channel_request_reply_success
|
||||
ssh_message_channel_request_subsystem
|
||||
ssh_message_channel_request_x11_auth_cookie
|
||||
ssh_message_channel_request_x11_auth_protocol
|
||||
ssh_message_channel_request_x11_screen_number
|
||||
ssh_message_channel_request_x11_single_connection
|
||||
ssh_message_free
|
||||
ssh_message_get
|
||||
ssh_message_global_request_address
|
||||
ssh_message_global_request_port
|
||||
ssh_message_global_request_reply_success
|
||||
ssh_message_reply_default
|
||||
ssh_message_retrieve
|
||||
ssh_message_service_reply_success
|
||||
ssh_message_service_service
|
||||
ssh_message_subtype
|
||||
ssh_message_type
|
||||
ssh_mkdir
|
||||
ssh_new
|
||||
ssh_options_copy
|
||||
ssh_options_get
|
||||
ssh_options_get_port
|
||||
ssh_options_getopt
|
||||
ssh_options_parse_config
|
||||
ssh_options_set
|
||||
ssh_pcap_file_close
|
||||
ssh_pcap_file_free
|
||||
ssh_pcap_file_new
|
||||
ssh_pcap_file_open
|
||||
ssh_pki_copy_cert_to_privkey
|
||||
ssh_pki_ctx_free
|
||||
ssh_pki_ctx_get_sk_attestation_buffer
|
||||
ssh_pki_ctx_new
|
||||
ssh_pki_ctx_options_set
|
||||
ssh_pki_ctx_set_sk_pin_callback
|
||||
ssh_pki_ctx_sk_callbacks_option_set
|
||||
ssh_pki_ctx_sk_callbacks_options_clear
|
||||
ssh_pki_export_privkey_base64
|
||||
ssh_pki_export_privkey_base64_format
|
||||
ssh_pki_export_privkey_file
|
||||
ssh_pki_export_privkey_file_format
|
||||
ssh_pki_export_privkey_to_pubkey
|
||||
ssh_pki_export_pubkey_base64
|
||||
ssh_pki_export_pubkey_file
|
||||
ssh_pki_generate
|
||||
ssh_pki_generate_key
|
||||
ssh_pki_import_cert_base64
|
||||
ssh_pki_import_cert_file
|
||||
ssh_pki_import_privkey_base64
|
||||
ssh_pki_import_privkey_file
|
||||
ssh_pki_import_pubkey_base64
|
||||
ssh_pki_import_pubkey_file
|
||||
ssh_pki_key_ecdsa_name
|
||||
ssh_print_hash
|
||||
ssh_print_hexa
|
||||
ssh_privatekey_type
|
||||
ssh_publickey_to_file
|
||||
ssh_remove_channel_callbacks
|
||||
ssh_request_no_more_sessions
|
||||
ssh_scp_accept_request
|
||||
ssh_scp_close
|
||||
ssh_scp_deny_request
|
||||
ssh_scp_free
|
||||
ssh_scp_init
|
||||
ssh_scp_leave_directory
|
||||
ssh_scp_new
|
||||
ssh_scp_pull_request
|
||||
ssh_scp_push_directory
|
||||
ssh_scp_push_file
|
||||
ssh_scp_push_file64
|
||||
ssh_scp_read
|
||||
ssh_scp_request_get_filename
|
||||
ssh_scp_request_get_permissions
|
||||
ssh_scp_request_get_size
|
||||
ssh_scp_request_get_size64
|
||||
ssh_scp_request_get_warning
|
||||
ssh_scp_write
|
||||
ssh_select
|
||||
ssh_send_debug
|
||||
ssh_send_ignore
|
||||
ssh_send_issue_banner
|
||||
ssh_send_keepalive
|
||||
ssh_server_init_kex
|
||||
ssh_service_request
|
||||
ssh_session_export_known_hosts_entry
|
||||
ssh_session_get_known_hosts_entry
|
||||
ssh_session_has_known_hosts_entry
|
||||
ssh_session_is_known_server
|
||||
ssh_session_set_disconnect_message
|
||||
ssh_session_update_known_hosts
|
||||
ssh_set_agent_channel
|
||||
ssh_set_agent_socket
|
||||
ssh_set_auth_methods
|
||||
ssh_set_blocking
|
||||
ssh_set_callbacks
|
||||
ssh_set_channel_callbacks
|
||||
ssh_set_counters
|
||||
ssh_set_fd_except
|
||||
ssh_set_fd_toread
|
||||
ssh_set_fd_towrite
|
||||
ssh_set_log_callback
|
||||
ssh_set_log_level
|
||||
ssh_set_log_userdata
|
||||
ssh_set_message_callback
|
||||
ssh_set_pcap_file
|
||||
ssh_set_server_callbacks
|
||||
ssh_silent_disconnect
|
||||
ssh_sk_resident_keys_load
|
||||
ssh_string_burn
|
||||
ssh_string_cmp
|
||||
ssh_string_copy
|
||||
ssh_string_data
|
||||
ssh_string_fill
|
||||
ssh_string_free
|
||||
ssh_string_free_char
|
||||
ssh_string_from_char
|
||||
ssh_string_from_data
|
||||
ssh_string_get_char
|
||||
ssh_string_len
|
||||
ssh_string_new
|
||||
ssh_string_to_char
|
||||
ssh_threads_get_default
|
||||
ssh_threads_get_noop
|
||||
ssh_threads_get_pthread
|
||||
ssh_threads_set_callbacks
|
||||
ssh_try_publickey_from_file
|
||||
ssh_userauth_agent
|
||||
ssh_userauth_agent_pubkey
|
||||
ssh_userauth_autopubkey
|
||||
ssh_userauth_gssapi
|
||||
ssh_userauth_kbdint
|
||||
ssh_userauth_kbdint_getanswer
|
||||
ssh_userauth_kbdint_getinstruction
|
||||
ssh_userauth_kbdint_getname
|
||||
ssh_userauth_kbdint_getnanswers
|
||||
ssh_userauth_kbdint_getnprompts
|
||||
ssh_userauth_kbdint_getprompt
|
||||
ssh_userauth_kbdint_setanswer
|
||||
ssh_userauth_list
|
||||
ssh_userauth_none
|
||||
ssh_userauth_offer_pubkey
|
||||
ssh_userauth_password
|
||||
ssh_userauth_privatekey_file
|
||||
ssh_userauth_pubkey
|
||||
ssh_userauth_publickey
|
||||
ssh_userauth_publickey_auto
|
||||
ssh_userauth_publickey_auto_get_current_identity
|
||||
ssh_userauth_try_publickey
|
||||
ssh_version
|
||||
ssh_vlog
|
||||
ssh_write_knownhost
|
||||
sshsig_sign
|
||||
sshsig_verify
|
||||
string_burn
|
||||
string_copy
|
||||
string_data
|
||||
string_fill
|
||||
string_free
|
||||
string_from_char
|
||||
string_len
|
||||
string_new
|
||||
string_to_char
|
||||
88
src/agent.c
88
src/agent.c
@@ -635,3 +635,91 @@ ssh_string ssh_agent_sign_data(ssh_session session,
|
||||
|
||||
return sig_blob;
|
||||
}
|
||||
|
||||
int ssh_agent_remove_identity(ssh_session session,
|
||||
const ssh_key key)
|
||||
{
|
||||
ssh_buffer request = NULL;
|
||||
ssh_buffer reply = NULL;
|
||||
ssh_string key_blob = NULL;
|
||||
uint8_t type = 0;
|
||||
int rc = SSH_ERROR;
|
||||
|
||||
if (session == NULL || key == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (session->agent == NULL) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"No agent connection available");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Connect to the agent if not already connected */
|
||||
if (!ssh_socket_is_open(session->agent->sock)) {
|
||||
if (agent_connect(session) < 0) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Could not connect to SSH agent");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
request = ssh_buffer_new();
|
||||
if (request == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ssh_buffer_add_u8(request, SSH2_AGENTC_REMOVE_IDENTITY) < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ssh_pki_export_pubkey_blob(key, &key_blob) < 0) {
|
||||
ssh_set_error(session, SSH_FATAL, "Failed to export public key blob");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ssh_buffer_add_ssh_string(request, key_blob) < 0) {
|
||||
ssh_set_error_oom(session);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
reply = ssh_buffer_new();
|
||||
if (reply == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (agent_talk(session, request, reply) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ssh_buffer_get_u8(reply, &type) != sizeof(uint8_t)) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Failed to read agent reply type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (agent_failed(type)) {
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Agent reports failure removing identity");
|
||||
goto fail;
|
||||
} else if (type != SSH_AGENT_SUCCESS) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"Agent refused to remove identity: reply type %u",
|
||||
type);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = SSH_OK;
|
||||
|
||||
fail:
|
||||
SSH_STRING_FREE(key_blob);
|
||||
SSH_BUFFER_FREE(request);
|
||||
SSH_BUFFER_FREE(reply);
|
||||
return rc;
|
||||
}
|
||||
|
||||
24
src/auth.c
24
src/auth.c
@@ -1397,7 +1397,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_INFO,
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Starting authentication as a user %s",
|
||||
username ? username : session->opts.username);
|
||||
|
||||
@@ -1835,6 +1835,14 @@ int ssh_userauth_agent_pubkey(ssh_session session,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Allocates memory for keyboard interactive auth structure.
|
||||
*
|
||||
* @return A newly allocated ssh_kbdint structure `kbd` on success, NULL on failure.
|
||||
* The caller is responsible for freeing allocated memory.
|
||||
*/
|
||||
ssh_kbdint ssh_kbdint_new(void)
|
||||
{
|
||||
ssh_kbdint kbd;
|
||||
@@ -1847,7 +1855,11 @@ ssh_kbdint ssh_kbdint_new(void)
|
||||
return kbd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Deallocate memory for keyboard interactive auth structure.
|
||||
*
|
||||
* @param[in] kbd The keyboard interactive structure to free.
|
||||
*/
|
||||
void ssh_kbdint_free(ssh_kbdint kbd)
|
||||
{
|
||||
size_t i, n;
|
||||
@@ -1885,6 +1897,14 @@ void ssh_kbdint_free(ssh_kbdint kbd)
|
||||
SAFE_FREE(kbd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clean a keyboard interactive auth structure.
|
||||
*
|
||||
* Clears structure's fields and resets nanswers and nprompts to 0, allowing
|
||||
* reuse.
|
||||
*
|
||||
* @param[in] kbd The keyboard interactive struct to clean
|
||||
*/
|
||||
void ssh_kbdint_clean(ssh_kbdint kbd)
|
||||
{
|
||||
size_t i, n;
|
||||
|
||||
@@ -327,7 +327,7 @@ static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, socket_t fd, int reve
|
||||
/** @internal
|
||||
* @brief returns the current poll handle, or creates it
|
||||
* @param sshbind the ssh_bind object
|
||||
* @returns a ssh_poll handle suitable for operation
|
||||
* @return a ssh_poll handle suitable for operation
|
||||
*/
|
||||
ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind)
|
||||
{
|
||||
|
||||
@@ -217,7 +217,7 @@ local_parse_file(ssh_bind bind,
|
||||
return;
|
||||
}
|
||||
|
||||
f = fopen(filename, "r");
|
||||
f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (f == NULL) {
|
||||
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
|
||||
filename);
|
||||
@@ -655,7 +655,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
|
||||
* option to be redefined later by another file. */
|
||||
uint8_t seen[BIND_CFG_MAX] = {0};
|
||||
|
||||
f = fopen(filename, "r");
|
||||
f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (f == NULL) {
|
||||
return 0;
|
||||
}
|
||||
@@ -676,12 +676,21 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration string and set the options to the given bind session
|
||||
/**
|
||||
* @brief Parse configuration string and set the options to the given bind
|
||||
* session
|
||||
*
|
||||
* @params[in] bind The ssh bind session
|
||||
* @params[in] input Null terminated string containing the configuration
|
||||
* @param[in] bind The ssh bind session
|
||||
* @param[in] input Null terminated string containing the configuration
|
||||
*
|
||||
* @returns SSH_OK on successful parsing the configuration string,
|
||||
* @warning Options set via this function may be overridden if a configuration
|
||||
* file is parsed afterwards (e.g., by an implicit call to
|
||||
* ssh_bind_options_parse_config() inside ssh_bind_listen(), or by a
|
||||
* manual call to the same function) and contains the same options.\n
|
||||
* It is the caller’s responsibility to ensure the correct order of
|
||||
* API calls if explicit options must take precedence.
|
||||
*
|
||||
* @return SSH_OK on successful parsing the configuration string,
|
||||
* SSH_ERROR on error
|
||||
*/
|
||||
int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
|
||||
@@ -713,21 +722,29 @@ int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
|
||||
}
|
||||
if (c == NULL) {
|
||||
/* should not happen, would mean a string without trailing '\0' */
|
||||
SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string");
|
||||
ssh_set_error(bind,
|
||||
SSH_FATAL,
|
||||
"No trailing '\\0' in config string");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
line_len = c - line_start;
|
||||
if (line_len > MAX_LINE_SIZE - 1) {
|
||||
SSH_LOG(SSH_LOG_WARN,
|
||||
"Line %u too long: %zu characters",
|
||||
line_num,
|
||||
line_len);
|
||||
ssh_set_error(bind,
|
||||
SSH_FATAL,
|
||||
"Line %u too long: %zu characters",
|
||||
line_num,
|
||||
line_len);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
memcpy(line, line_start, line_len);
|
||||
line[line_len] = '\0';
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
|
||||
rv = ssh_bind_config_parse_line(bind, line, line_num, &parser_flags, seen, 0);
|
||||
rv = ssh_bind_config_parse_line(bind,
|
||||
line,
|
||||
line_num,
|
||||
&parser_flags,
|
||||
seen,
|
||||
0);
|
||||
if (rv < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
39
src/buffer.c
39
src/buffer.c
@@ -109,6 +109,11 @@ static void buffer_verify(ssh_buffer buf)
|
||||
}
|
||||
|
||||
#else
|
||||
/** @internal
|
||||
* @brief No-op stub for buffer_verify when debug checks are disabled.
|
||||
*
|
||||
* @param x The buffer to verify (ignored).
|
||||
*/
|
||||
#define buffer_verify(x)
|
||||
#endif
|
||||
|
||||
@@ -921,18 +926,10 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
|
||||
va_arg(ap, void *);
|
||||
count++; /* increase argument count */
|
||||
break;
|
||||
case 'F':
|
||||
case 'B':
|
||||
b = va_arg(ap, bignum);
|
||||
if (*p == 'F') {
|
||||
/* For padded bignum, we know the exact length */
|
||||
len = va_arg(ap, size_t);
|
||||
count++; /* increase argument count */
|
||||
needed_size += sizeof(uint32_t) + len;
|
||||
} else {
|
||||
/* The bignum bytes + 1 for possible padding */
|
||||
needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
|
||||
}
|
||||
/* The bignum bytes + 1 for possible padding */
|
||||
needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
|
||||
break;
|
||||
case 't':
|
||||
cstring = va_arg(ap, char *);
|
||||
@@ -972,9 +969,12 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
|
||||
|
||||
/** @internal
|
||||
* @brief Add multiple values in a buffer on a single function call
|
||||
*
|
||||
* @param[in] buffer The buffer to add to
|
||||
* @param[in] format A format string of arguments.
|
||||
* @param[in] argc Number of arguments passed after format.
|
||||
* @param[in] ap A va_list of arguments.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @see ssh_buffer_add_format() for format list values.
|
||||
@@ -1062,16 +1062,9 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
|
||||
rc = ssh_buffer_add_data(buffer, o.data, (uint32_t)len);
|
||||
o.data = NULL;
|
||||
break;
|
||||
case 'F':
|
||||
case 'B':
|
||||
b = va_arg(ap, bignum);
|
||||
if (*p == 'F') {
|
||||
len = va_arg(ap, size_t);
|
||||
count++; /* increase argument count */
|
||||
o.string = ssh_make_padded_bignum_string(b, len);
|
||||
} else {
|
||||
o.string = ssh_make_bignum_string(b);
|
||||
}
|
||||
o.string = ssh_make_bignum_string(b);
|
||||
if(o.string == NULL){
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
@@ -1127,8 +1120,9 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
|
||||
* 'P': size_t, void * (len of data, pointer to data)
|
||||
* only pushes data.
|
||||
* 'B': bignum (pushed as SSH string)
|
||||
* 'F': bignum, size_t (bignum, padded to fixed length,
|
||||
* pushed as SSH string)
|
||||
* @param[in] argc Number of arguments passed after format.
|
||||
* @param[in] ... Arguments as described by the format string.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @warning when using 'P' with a constant size (e.g. 8), do not
|
||||
@@ -1165,7 +1159,9 @@ int _ssh_buffer_pack(struct ssh_buffer_struct *buffer,
|
||||
* @brief Get multiple values from a buffer on a single function call
|
||||
* @param[in] buffer The buffer to get from
|
||||
* @param[in] format A format string of arguments.
|
||||
* @param[in] argc Number of arguments passed in the va_list.
|
||||
* @param[in] ap A va_list of arguments.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @see ssh_buffer_get_format() for format list values.
|
||||
@@ -1428,6 +1424,9 @@ cleanup:
|
||||
* 'P': size_t, void ** (len of data, pointer to data)
|
||||
* only pulls data.
|
||||
* 'B': bignum * (pulled as SSH string)
|
||||
* @param[in] argc Number of arguments passed after format.
|
||||
* @param[in] ... Arguments as described by the format string.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @warning when using 'P' with a constant size (e.g. 8), do not
|
||||
|
||||
@@ -1738,7 +1738,7 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
|
||||
*/
|
||||
int ssh_channel_is_open(ssh_channel channel)
|
||||
{
|
||||
if (channel == NULL) {
|
||||
if (channel == NULL || channel->session == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0);
|
||||
@@ -3517,7 +3517,7 @@ int ssh_channel_get_exit_state(ssh_channel channel,
|
||||
*pexit_signal = NULL;
|
||||
if (channel->exit.signal != NULL) {
|
||||
*pexit_signal = strdup(channel->exit.signal);
|
||||
if (pexit_signal == NULL) {
|
||||
if (*pexit_signal == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
@@ -582,9 +582,8 @@ int ssh_connect(ssh_session session)
|
||||
session->client = 1;
|
||||
|
||||
if (session->opts.fd == SSH_INVALID_SOCKET &&
|
||||
session->opts.host == NULL &&
|
||||
session->opts.ProxyCommand == NULL)
|
||||
{
|
||||
session->opts.originalhost == NULL &&
|
||||
session->opts.ProxyCommand == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Hostname required");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
84
src/config.c
84
src/config.c
@@ -256,9 +256,10 @@ local_parse_file(ssh_session session,
|
||||
return;
|
||||
}
|
||||
|
||||
f = fopen(filename, "r");
|
||||
f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (f == NULL) {
|
||||
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
|
||||
SSH_LOG(SSH_LOG_RARE,
|
||||
"Failed to open included configuration file %s",
|
||||
filename);
|
||||
return;
|
||||
}
|
||||
@@ -493,6 +494,10 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
bool parse_entry = do_parsing;
|
||||
bool libssh_proxy_jump = ssh_libssh_proxy_jumps();
|
||||
|
||||
if (do_parsing) {
|
||||
SAFE_FREE(session->opts.proxy_jumps_str);
|
||||
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||
}
|
||||
/* Special value none disables the proxy */
|
||||
cmp = strcasecmp(s, "none");
|
||||
if (cmp == 0) {
|
||||
@@ -509,6 +514,17 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (do_parsing) {
|
||||
/* Store the whole string in session */
|
||||
SAFE_FREE(session->opts.proxy_jumps_str);
|
||||
session->opts.proxy_jumps_str = strdup(s);
|
||||
if (session->opts.proxy_jumps_str == NULL) {
|
||||
free(c);
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
cp = c;
|
||||
do {
|
||||
endp = strchr(cp, ',');
|
||||
@@ -528,6 +544,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
&jump_host->username,
|
||||
&jump_host->hostname,
|
||||
&port,
|
||||
false,
|
||||
false);
|
||||
if (rv != SSH_OK) {
|
||||
ssh_set_error_invalid(session);
|
||||
@@ -550,7 +567,12 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
}
|
||||
} else if (parse_entry) {
|
||||
/* We actually care only about the first item */
|
||||
rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
|
||||
rv = ssh_config_parse_uri(cp,
|
||||
&username,
|
||||
&hostname,
|
||||
&port,
|
||||
false,
|
||||
false);
|
||||
if (rv != SSH_OK) {
|
||||
ssh_set_error_invalid(session);
|
||||
goto out;
|
||||
@@ -566,7 +588,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
}
|
||||
} else {
|
||||
/* The rest is just sanity-checked to avoid failures later */
|
||||
rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
|
||||
rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false, false);
|
||||
if (rv != SSH_OK) {
|
||||
ssh_set_error_invalid(session);
|
||||
goto out;
|
||||
@@ -1024,20 +1046,20 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
break;
|
||||
|
||||
case MATCH_ORIGINALHOST:
|
||||
/* Skip one argument */
|
||||
/* Here we match only one argument */
|
||||
p = ssh_config_get_str_tok(&s, NULL);
|
||||
if (p == NULL || p[0] == '\0') {
|
||||
SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
|
||||
"'%s' requires argument", count, p2);
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"line %d: ERROR - Match originalhost keyword "
|
||||
"requires argument",
|
||||
count);
|
||||
SAFE_FREE(x);
|
||||
return -1;
|
||||
}
|
||||
result &=
|
||||
ssh_config_match(session->opts.originalhost, p, negate);
|
||||
args++;
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"line %d: Unsupported Match keyword '%s', ignoring",
|
||||
count,
|
||||
p2);
|
||||
result = 0;
|
||||
break;
|
||||
|
||||
case MATCH_HOST:
|
||||
@@ -1050,7 +1072,11 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
SAFE_FREE(x);
|
||||
return -1;
|
||||
}
|
||||
result &= ssh_config_match(session->opts.host, p, negate);
|
||||
result &= ssh_config_match(session->opts.host
|
||||
? session->opts.host
|
||||
: session->opts.originalhost,
|
||||
p,
|
||||
negate);
|
||||
args++;
|
||||
break;
|
||||
|
||||
@@ -1138,7 +1164,9 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
int ok = 0, result = -1;
|
||||
|
||||
*parsing = 0;
|
||||
lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL;
|
||||
lowerhost = (session->opts.originalhost)
|
||||
? ssh_lowercase(session->opts.originalhost)
|
||||
: NULL;
|
||||
for (p = ssh_config_get_str_tok(&s, NULL);
|
||||
p != NULL && p[0] != '\0';
|
||||
p = ssh_config_get_str_tok(&s, NULL)) {
|
||||
@@ -1165,7 +1193,9 @@ static int ssh_config_parse_line_internal(ssh_session session,
|
||||
if (z == NULL) {
|
||||
z = strdup(p);
|
||||
}
|
||||
session->opts.config_hostname_only = true;
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, z);
|
||||
session->opts.config_hostname_only = false;
|
||||
free(z);
|
||||
}
|
||||
break;
|
||||
@@ -1654,11 +1684,12 @@ int ssh_config_parse_line_cli(ssh_session session, const char *line)
|
||||
true);
|
||||
}
|
||||
|
||||
/* @brief Parse configuration from a file pointer
|
||||
/**
|
||||
* @brief Parse configuration from a file pointer
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] fp A valid file pointer
|
||||
* @params[in] global Whether the config is global or not
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] fp A valid file pointer
|
||||
* @param[in] global Whether the config is global or not
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
@@ -1680,10 +1711,11 @@ int ssh_config_parse(ssh_session session, FILE *fp, bool global)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration file and set the options to the given session
|
||||
/**
|
||||
* @brief Parse configuration file and set the options to the given session
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] filename The path to the ssh configuration file
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] filename The path to the ssh configuration file
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
@@ -1693,8 +1725,9 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
|
||||
int rv;
|
||||
bool global = 0;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (fp == NULL) {
|
||||
/* The underlying function logs the reasons */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1717,10 +1750,11 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration string and set the options to the given session
|
||||
/**
|
||||
* @brief Parse configuration string and set the options to the given session
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] input Null terminated string containing the configuration
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] input Null terminated string containing the configuration
|
||||
*
|
||||
* @returns SSH_OK on successful parsing the configuration string,
|
||||
* SSH_ERROR on error
|
||||
|
||||
@@ -172,7 +172,8 @@ int ssh_config_parse_uri(const char *tok,
|
||||
char **username,
|
||||
char **hostname,
|
||||
char **port,
|
||||
bool ignore_port)
|
||||
bool ignore_port,
|
||||
bool strict)
|
||||
{
|
||||
char *endp = NULL;
|
||||
long port_n;
|
||||
@@ -243,13 +244,31 @@ int ssh_config_parse_uri(const char *tok,
|
||||
if (*hostname == NULL) {
|
||||
goto error;
|
||||
}
|
||||
/* if not an ip, check syntax */
|
||||
rc = ssh_is_ipaddr(*hostname);
|
||||
if (rc == 0) {
|
||||
rc = ssh_check_hostname_syntax(*hostname);
|
||||
if (rc != SSH_OK) {
|
||||
if (strict) {
|
||||
/* if not an ip, check syntax */
|
||||
rc = ssh_is_ipaddr(*hostname);
|
||||
if (rc == 0) {
|
||||
rc = ssh_check_hostname_syntax(*hostname);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Reject shell metacharacters to allow config aliases with
|
||||
* non-RFC1035 chars (e.g. %, _). Modeled on OpenSSH's
|
||||
* valid_hostname() in ssh.c. */
|
||||
const char *c = NULL;
|
||||
if ((*hostname)[0] == '-') {
|
||||
goto error;
|
||||
}
|
||||
for (c = *hostname; *c != '\0'; c++) {
|
||||
char *is_meta = strchr("'`\"$\\;&<>|(){},", *c);
|
||||
int is_space = isspace((unsigned char)*c);
|
||||
int is_ctrl = iscntrl((unsigned char)*c);
|
||||
if (is_meta != NULL || is_space || is_ctrl) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Skip also the closing bracket */
|
||||
|
||||
@@ -305,6 +305,87 @@ static void ssh_connector_reset_pollevents(ssh_connector connector)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Update the connector's flags after a read-write io
|
||||
* operation
|
||||
*
|
||||
* This should be called after some data is successfully read from
|
||||
* connector's input and written to connector's output.
|
||||
*
|
||||
* @param[in, out] connector Connector for which the io operation occurred.
|
||||
*
|
||||
* @warning This does not consider the case when the io indicated failure
|
||||
*
|
||||
* @warning This does not consider the case when the input indicated that
|
||||
* EOF was encountered.
|
||||
*/
|
||||
static void ssh_connector_update_flags_after_io(ssh_connector connector)
|
||||
{
|
||||
/*
|
||||
* With fds we can afford to mark:
|
||||
* - in_available as 0 after an fd read (even if more pending data can be
|
||||
* immediately read from the fd)
|
||||
*
|
||||
* - out_wontblock as 0 after an fd write (even if more data can
|
||||
* be written to the fd without blocking)
|
||||
*
|
||||
* since poll events set on the fd will get raised to indicate
|
||||
* possibility of read/write in case existing situation is apt
|
||||
* (i.e can read/write occur right now) or if situation becomes
|
||||
* apt in future (read data becomes available, write becomes
|
||||
* possible)
|
||||
*/
|
||||
|
||||
/*
|
||||
* On the other hand, with channels we need to be more careful
|
||||
* before claiming read/write not possible because channel callbacks
|
||||
* are called in limited scenarios.
|
||||
*
|
||||
* (e.g. connector callback to indicate read data available on input
|
||||
* channel is called only when new data is received on channel. It is
|
||||
* not called when we have some pending data in channel's buffers but
|
||||
* don't receive any new data on the channel)
|
||||
*
|
||||
* Hence, in case of channels, blindly setting flag associated with
|
||||
* read/write input/output to 0 after a read/write may not be a good
|
||||
* idea as the callback that sets it back to 1 again may not be ever
|
||||
* called again.
|
||||
*/
|
||||
|
||||
uint32_t window_size;
|
||||
|
||||
/* update in_available based on input source (fd or channel) */
|
||||
if (connector->in_fd != SSH_INVALID_SOCKET) {
|
||||
connector->in_available = 0;
|
||||
} else if (connector->in_channel != NULL) {
|
||||
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0) {
|
||||
connector->in_available = 1;
|
||||
} else {
|
||||
connector->in_available = 0;
|
||||
}
|
||||
} else {
|
||||
/* connector input is invalid ! */
|
||||
return;
|
||||
}
|
||||
|
||||
/* update out_wontblock based on output source (fd or channel) */
|
||||
if (connector->out_fd != SSH_INVALID_SOCKET) {
|
||||
connector->out_wontblock = 0;
|
||||
} else if (connector->out_channel != NULL) {
|
||||
window_size = ssh_channel_window_size(connector->out_channel);
|
||||
if (window_size > 0) {
|
||||
connector->out_wontblock = 1;
|
||||
} else {
|
||||
connector->out_wontblock = 0;
|
||||
}
|
||||
} else {
|
||||
/* connector output is invalid ! */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@@ -390,8 +471,8 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
|
||||
ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
|
||||
return;
|
||||
}
|
||||
connector->out_wontblock = 0;
|
||||
connector->in_available = 0;
|
||||
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
} else {
|
||||
connector->in_available = 1;
|
||||
}
|
||||
@@ -444,8 +525,8 @@ ssh_connector_fd_out_cb(ssh_connector connector)
|
||||
"Output socket or channel closed");
|
||||
return;
|
||||
}
|
||||
connector->in_available = 0;
|
||||
connector->out_wontblock = 0;
|
||||
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
} else {
|
||||
connector->out_wontblock = 1;
|
||||
}
|
||||
@@ -566,11 +647,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
connector->out_wontblock = 0;
|
||||
connector->in_available = 0;
|
||||
if ((unsigned int)w < len) {
|
||||
connector->in_available = 1;
|
||||
}
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
ssh_connector_reset_pollevents(connector);
|
||||
|
||||
return w;
|
||||
@@ -642,8 +719,8 @@ ssh_connector_channel_write_wontblock_cb(ssh_session session,
|
||||
|
||||
return 0;
|
||||
}
|
||||
connector->in_available = 0;
|
||||
connector->out_wontblock = 0;
|
||||
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
} else {
|
||||
connector->out_wontblock = 1;
|
||||
}
|
||||
|
||||
@@ -526,9 +526,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
|
||||
}
|
||||
|
||||
if (moduli_file != NULL)
|
||||
moduli = fopen(moduli_file, "r");
|
||||
moduli = ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
else
|
||||
moduli = fopen(MODULI_FILE, "r");
|
||||
moduli = ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
|
||||
if (moduli == NULL) {
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
|
||||
@@ -43,11 +43,9 @@
|
||||
* @brief Registers an error with a description.
|
||||
*
|
||||
* @param error The place to store the error.
|
||||
*
|
||||
* @param code The class of error.
|
||||
*
|
||||
* @param function The name of the calling function.
|
||||
* @param descr The description, which can be a format string.
|
||||
*
|
||||
* @param ... The arguments for the format string.
|
||||
*/
|
||||
void _ssh_set_error(void *error,
|
||||
@@ -76,6 +74,7 @@ void _ssh_set_error(void *error,
|
||||
* @brief Registers an out of memory error
|
||||
*
|
||||
* @param error The place to store the error.
|
||||
* @param function The name of the calling function.
|
||||
*
|
||||
*/
|
||||
void _ssh_set_error_oom(void *error, const char *function)
|
||||
|
||||
28
src/gssapi.c
28
src/gssapi.c
@@ -198,7 +198,7 @@ int
|
||||
ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
uint32_t n_oid, ssh_string *oids)
|
||||
{
|
||||
char hostname[NI_MAXHOST] = {0};
|
||||
char *hostname = NULL;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
size_t i;
|
||||
gss_OID_set supported; /* oids supported by server */
|
||||
@@ -210,14 +210,6 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
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);
|
||||
@@ -251,6 +243,7 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
/* Get the server supported oids */
|
||||
rc = ssh_gssapi_server_oids(&supported);
|
||||
if (rc != SSH_OK) {
|
||||
gss_release_oid_set(&min_stat, &both_supported);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
@@ -284,7 +277,16 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
hostname = ssh_get_local_hostname();
|
||||
if (hostname == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_import_name(session->gssapi, hostname);
|
||||
SAFE_FREE(hostname);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_auth_reply_default(session, 0);
|
||||
gss_release_oid_set(&min_stat, &both_supported);
|
||||
@@ -729,7 +731,8 @@ int ssh_gssapi_check_client_config(ssh_session session)
|
||||
gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
|
||||
if (gssapi == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
ret = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
gssapi->server_creds = GSS_C_NO_CREDENTIAL;
|
||||
gssapi->client_creds = GSS_C_NO_CREDENTIAL;
|
||||
@@ -818,6 +821,11 @@ int ssh_gssapi_check_client_config(ssh_session session)
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
gss_delete_sec_context(&min_stat, &gssapi->ctx, GSS_C_NO_BUFFER);
|
||||
|
||||
if (client_id != GSS_C_NO_NAME) {
|
||||
gss_release_name(&min_stat, &client_id);
|
||||
client_id = GSS_C_NO_NAME;
|
||||
}
|
||||
|
||||
SAFE_FREE(gssapi->canonic_user);
|
||||
SAFE_FREE(gssapi);
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ static int derive_hybrid_secret(ssh_session session,
|
||||
|
||||
rc = ssh_buffer_pack(combined_secret,
|
||||
"PP",
|
||||
MLKEM_SHARED_SECRET_SIZE,
|
||||
(size_t)MLKEM_SHARED_SECRET_SIZE,
|
||||
mlkem_shared_secret,
|
||||
ssh_string_len(ecdh_shared_secret),
|
||||
ssh_string_data(ecdh_shared_secret));
|
||||
@@ -244,7 +244,7 @@ int ssh_client_hybrid_mlkem_init(ssh_session session)
|
||||
"PP",
|
||||
ssh_string_len(crypto->mlkem_client_pubkey),
|
||||
ssh_string_data(crypto->mlkem_client_pubkey),
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE,
|
||||
crypto->curve25519_client_pubkey);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
@@ -768,7 +768,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
|
||||
"PP",
|
||||
ssh_string_len(crypto->mlkem_ciphertext),
|
||||
ssh_string_data(crypto->mlkem_ciphertext),
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE,
|
||||
crypto->curve25519_server_pubkey);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
|
||||
@@ -421,7 +421,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
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 *hostname = NULL;
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "S", &otoken);
|
||||
@@ -538,8 +538,8 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = gethostname(hostname, 64);
|
||||
if (rc != 0) {
|
||||
hostname = ssh_get_local_hostname();
|
||||
if (hostname == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
@@ -547,6 +547,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_import_name(session->gssapi, hostname);
|
||||
SAFE_FREE(hostname);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
@@ -590,6 +591,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
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");
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
goto error;
|
||||
}
|
||||
SSH_LOG(SSH_LOG_DEBUG, "token accepted");
|
||||
@@ -606,6 +608,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
"creating mic failed",
|
||||
maj_stat,
|
||||
min_stat);
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -620,15 +623,14 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
output_token.length,
|
||||
(size_t)output_token.length,
|
||||
output_token.value);
|
||||
gss_release_buffer(&min_stat, &output_token);
|
||||
gss_release_buffer(&min_stat, &mic);
|
||||
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;
|
||||
|
||||
@@ -1688,11 +1688,6 @@ int ssh_make_sessionid(ssh_session session)
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"F",
|
||||
session->next_crypto->shared_secret,
|
||||
SHA512_DIGEST_LEN);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
@@ -1919,9 +1914,6 @@ int ssh_generate_session_keys(ssh_session session)
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512:
|
||||
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
|
||||
k_string = ssh_make_padded_bignum_string(crypto->shared_secret,
|
||||
crypto->digest_len);
|
||||
break;
|
||||
case SSH_KEX_MLKEM768X25519_SHA256:
|
||||
case SSH_KEX_MLKEM768NISTP256_SHA256:
|
||||
#ifdef HAVE_MLKEM1024
|
||||
|
||||
@@ -83,7 +83,7 @@ static struct ssh_tokens_st *ssh_get_knownhost_line(FILE **file,
|
||||
struct ssh_tokens_st *tokens = NULL;
|
||||
|
||||
if (*file == NULL) {
|
||||
*file = fopen(filename,"r");
|
||||
*file = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (*file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ static int ssh_known_hosts_read_entries(const char *match,
|
||||
FILE *fp = NULL;
|
||||
int rc;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (fp == NULL) {
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s",
|
||||
@@ -310,7 +310,11 @@ static int ssh_known_hosts_read_entries(const char *match,
|
||||
}
|
||||
}
|
||||
if (entry != NULL) {
|
||||
ssh_list_append(*entries, entry);
|
||||
rc = ssh_list_append(*entries, entry);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_knownhosts_entry_free(entry);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
19
src/legacy.c
19
src/legacy.c
@@ -615,10 +615,10 @@ int ssh_publickey_to_file(ssh_session session,
|
||||
FILE *fp = NULL;
|
||||
char *user = NULL;
|
||||
char buffer[1024];
|
||||
char host[256];
|
||||
char *host = NULL;
|
||||
unsigned char *pubkey_64 = NULL;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
if(session==NULL)
|
||||
return SSH_ERROR;
|
||||
if(file==NULL || pubkey==NULL){
|
||||
@@ -636,8 +636,8 @@ int ssh_publickey_to_file(ssh_session session,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = gethostname(host, sizeof(host));
|
||||
if (rc < 0) {
|
||||
host = ssh_get_local_hostname();
|
||||
if (host == NULL) {
|
||||
SAFE_FREE(user);
|
||||
SAFE_FREE(pubkey_64);
|
||||
return SSH_ERROR;
|
||||
@@ -651,6 +651,7 @@ int ssh_publickey_to_file(ssh_session session,
|
||||
|
||||
SAFE_FREE(pubkey_64);
|
||||
SAFE_FREE(user);
|
||||
SAFE_FREE(host);
|
||||
|
||||
SSH_LOG(SSH_LOG_RARE, "Trying to write public key file: %s", file);
|
||||
SSH_LOG(SSH_LOG_PACKET, "public key file content: %s", buffer);
|
||||
@@ -769,6 +770,16 @@ ssh_string ssh_get_pubkey(ssh_session session)
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
/**
|
||||
* @brief Accept an incoming SSH connection on a bind socket.
|
||||
*
|
||||
* @deprecated Use ssh_bind_accept() instead.
|
||||
*
|
||||
* @param session The SSH session to accept the connection on.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*/
|
||||
int ssh_accept(ssh_session session) {
|
||||
return ssh_handle_key_exchange(session);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# This map file was updated with abimap-0.3.2
|
||||
# This map file was updated with abimap-0.4.0
|
||||
|
||||
LIBSSH_4_5_0 # Released
|
||||
{
|
||||
@@ -482,27 +482,34 @@ LIBSSH_4_10_0 # Released
|
||||
ssh_request_no_more_sessions;
|
||||
} LIBSSH_4_9_0;
|
||||
|
||||
LIBSSH_AFTER_4_10_0
|
||||
LIBSSH_4_11_0 # Released
|
||||
{
|
||||
global:
|
||||
sftp_get_users_groups_by_id;
|
||||
sftp_name_id_map_free;
|
||||
sftp_name_id_map_new;
|
||||
ssh_get_supported_methods;
|
||||
sshsig_sign;
|
||||
sshsig_verify;
|
||||
ssh_string_cmp;
|
||||
ssh_string_from_data;
|
||||
ssh_pki_ctx_new;
|
||||
ssh_key_get_sk_application;
|
||||
ssh_key_get_sk_flags;
|
||||
ssh_key_get_sk_user_id;
|
||||
ssh_pki_ctx_free;
|
||||
ssh_pki_ctx_get_sk_attestation_buffer;
|
||||
ssh_pki_ctx_new;
|
||||
ssh_pki_ctx_options_set;
|
||||
ssh_pki_ctx_set_sk_pin_callback;
|
||||
ssh_pki_ctx_sk_callbacks_option_set;
|
||||
ssh_pki_ctx_sk_callbacks_options_clear;
|
||||
ssh_pki_ctx_get_sk_attestation_buffer;
|
||||
ssh_key_get_sk_flags;
|
||||
ssh_key_get_sk_application;
|
||||
ssh_key_get_sk_user_id;
|
||||
ssh_pki_generate_key;
|
||||
ssh_sk_resident_keys_load;
|
||||
ssh_string_cmp;
|
||||
ssh_string_from_data;
|
||||
sshsig_sign;
|
||||
sshsig_verify;
|
||||
} LIBSSH_4_10_0;
|
||||
|
||||
LIBSSH_AFTER_4_11_0
|
||||
{
|
||||
global:
|
||||
ssh_bind_config_parse_string;
|
||||
} LIBSSH_4_11_0;
|
||||
|
||||
|
||||
51
src/log.c
51
src/log.c
@@ -109,6 +109,16 @@ static void ssh_log_custom(ssh_logging_callback log_fn,
|
||||
log_fn(verbosity, function, buf, ssh_get_log_userdata());
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Dispatch a pre-formatted log message to the active logging backend.
|
||||
*
|
||||
* If a custom logging callback is registered, the message is passed to it.
|
||||
* Otherwise, the message is written to stderr.
|
||||
*
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param function The name of the calling function.
|
||||
* @param buffer The already-formatted log message string.
|
||||
*/
|
||||
void ssh_log_function(int verbosity,
|
||||
const char *function,
|
||||
const char *buffer)
|
||||
@@ -123,6 +133,15 @@ void ssh_log_function(int verbosity,
|
||||
ssh_log_stderr(verbosity, function, buffer);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Format a log message from a va_list and dispatch it for logging.
|
||||
*
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param function The name of the calling function.
|
||||
* @param format A printf-style format string.
|
||||
* @param va Pointer to the variable argument list
|
||||
* for the format string.
|
||||
*/
|
||||
void ssh_vlog(int verbosity,
|
||||
const char *function,
|
||||
const char *format,
|
||||
@@ -134,6 +153,15 @@ void ssh_vlog(int verbosity,
|
||||
ssh_log_function(verbosity, function, buffer);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Log a message if the given verbosity does not
|
||||
* exceed the global log level.
|
||||
*
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param function The name of the calling function.
|
||||
* @param format A printf-style format string.
|
||||
* @param ... Additional arguments corresponding to the format string.
|
||||
*/
|
||||
void _ssh_log(int verbosity,
|
||||
const char *function,
|
||||
const char *format, ...)
|
||||
@@ -149,6 +177,15 @@ void _ssh_log(int verbosity,
|
||||
|
||||
/* LEGACY */
|
||||
|
||||
/** @brief Log a message using the verbosity level of the given session.
|
||||
*
|
||||
* @deprecated Use the SSH_LOG() macro instead.
|
||||
*
|
||||
* @param session The SSH session whose verbosity level is checked.
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param format A printf-style format string.
|
||||
* @param ... Arguments as described by the format string.
|
||||
*/
|
||||
void ssh_log(ssh_session session,
|
||||
int verbosity,
|
||||
const char *format, ...)
|
||||
@@ -164,9 +201,14 @@ void ssh_log(ssh_session session,
|
||||
|
||||
/** @internal
|
||||
* @brief log a SSH event with a common pointer
|
||||
* @param common The SSH/bind session.
|
||||
* @param verbosity The verbosity of the event.
|
||||
* @param format The format string of the log entry.
|
||||
*
|
||||
* Works for both ssh_session and ssh_bind as both embed ssh_common_struct.
|
||||
*
|
||||
* @param common The SSH/bind session.
|
||||
* @param verbosity The verbosity of the event.
|
||||
* @param function The name of the calling function.
|
||||
* @param format The format string of the log entry.
|
||||
* @param ... Additional arguments corresponding to the format string.
|
||||
*/
|
||||
void ssh_log_common(struct ssh_common_struct *common,
|
||||
int verbosity,
|
||||
@@ -221,6 +263,9 @@ int ssh_set_log_callback(ssh_logging_callback cb) {
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Clear the thread-local logging callback, reverting to stderr logging.
|
||||
*/
|
||||
void
|
||||
_ssh_reset_log_cb(void)
|
||||
{
|
||||
|
||||
111
src/match.c
111
src/match.c
@@ -53,85 +53,70 @@
|
||||
|
||||
#include "libssh/priv.h"
|
||||
|
||||
#define MAX_MATCH_RECURSION 16
|
||||
|
||||
/*
|
||||
* Returns true if the given string matches the pattern (which may contain ?
|
||||
* and * as wildcards), and zero if it does not match.
|
||||
/**
|
||||
* @brief Compare a string with a pattern containing wildcards `*` and `?`
|
||||
*
|
||||
* This function is an iterative replacement for the previously recursive
|
||||
* implementation to avoid exponential complexity (DoS) with specific patterns.
|
||||
*
|
||||
* @param[in] s The string to match.
|
||||
* @param[in] pattern The pattern to match against.
|
||||
*
|
||||
* @return 1 if the pattern matches, 0 otherwise.
|
||||
*/
|
||||
static int match_pattern(const char *s, const char *pattern, size_t limit)
|
||||
static int match_pattern(const char *s, const char *pattern)
|
||||
{
|
||||
bool had_asterisk = false;
|
||||
const char *s_star = NULL; /* Position in s when last `*` was met */
|
||||
const char *p_star = NULL; /* Position in pattern after last `*` */
|
||||
|
||||
if (s == NULL || pattern == NULL || limit <= 0) {
|
||||
if (s == NULL || pattern == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* If at end of pattern, accept if also at end of string. */
|
||||
if (*pattern == '\0') {
|
||||
return (*s == '\0');
|
||||
}
|
||||
|
||||
/* Skip all the asterisks and adjacent question marks */
|
||||
while (*pattern == '*' || (had_asterisk && *pattern == '?')) {
|
||||
if (*pattern == '*') {
|
||||
had_asterisk = true;
|
||||
}
|
||||
while (*s) {
|
||||
/* Case 1: Exact match or '?' wildcard */
|
||||
if (*pattern == *s || *pattern == '?') {
|
||||
s++;
|
||||
pattern++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (had_asterisk) {
|
||||
/* If at end of pattern, accept immediately. */
|
||||
if (!*pattern)
|
||||
return 1;
|
||||
|
||||
/* If next character in pattern is known, optimize. */
|
||||
if (*pattern != '?') {
|
||||
/*
|
||||
* Look instances of the next character in
|
||||
* pattern, and try to match starting from
|
||||
* those.
|
||||
*/
|
||||
for (; *s; s++)
|
||||
if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) {
|
||||
return 1;
|
||||
}
|
||||
/* Failed. */
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Move ahead one character at a time and try to
|
||||
* match at each position.
|
||||
/* Case 2: '*' wildcard */
|
||||
if (*pattern == '*') {
|
||||
/* Record the position of the star and the current string position.
|
||||
* We optimistically assume * matches 0 characters first.
|
||||
*/
|
||||
for (; *s; s++) {
|
||||
if (match_pattern(s, pattern, limit - 1)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* Failed. */
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* There must be at least one more character in the string.
|
||||
* If we are at the end, fail.
|
||||
*/
|
||||
if (!*s) {
|
||||
return 0;
|
||||
p_star = ++pattern;
|
||||
s_star = s;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check if the next character of the string is acceptable. */
|
||||
if (*pattern != '?' && *pattern != *s) {
|
||||
return 0;
|
||||
/* Case 3: Mismatch */
|
||||
if (p_star) {
|
||||
/* If we have seen a star previously, backtrack.
|
||||
* We restore the pattern to just after the star,
|
||||
* but advance the string position (consume one more char for the
|
||||
* star).
|
||||
* No need to backtrack to previous stars as any match of the last
|
||||
* star could be eaten the same way by the previous star.
|
||||
*/
|
||||
pattern = p_star;
|
||||
s = ++s_star;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Move to the next character, both in string and in pattern. */
|
||||
s++;
|
||||
/* Case 4: Mismatch and no star to backtrack to */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle trailing stars in the pattern
|
||||
* (e.g., pattern "abc*" matching "abc") */
|
||||
while (*pattern == '*') {
|
||||
pattern++;
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
return 0;
|
||||
/* If we reached the end of the pattern, it's a match */
|
||||
return (*pattern == '\0');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -182,7 +167,7 @@ int match_pattern_list(const char *string, const char *pattern,
|
||||
sub[subi] = '\0';
|
||||
|
||||
/* Try to match the subpattern against the string. */
|
||||
if (match_pattern(string, sub, MAX_MATCH_RECURSION)) {
|
||||
if (match_pattern(string, sub)) {
|
||||
if (negated) {
|
||||
return -1; /* Negative */
|
||||
} else {
|
||||
|
||||
563
src/misc.c
563
src/misc.c
@@ -37,6 +37,7 @@
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -108,25 +109,31 @@
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
char *ssh_get_user_home_dir(void)
|
||||
static char *ssh_get_user_home_dir_internal(void)
|
||||
{
|
||||
char tmp[PATH_MAX] = {0};
|
||||
char *szPath = NULL;
|
||||
char tmp[PATH_MAX] = {0};
|
||||
char *szPath = NULL;
|
||||
|
||||
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
|
||||
szPath = malloc(strlen(tmp) + 1);
|
||||
if (szPath == NULL) {
|
||||
return NULL;
|
||||
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
|
||||
szPath = malloc(strlen(tmp) + 1);
|
||||
if (szPath == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(szPath, tmp);
|
||||
return szPath;
|
||||
}
|
||||
|
||||
strcpy(szPath, tmp);
|
||||
return szPath;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we have read access on file */
|
||||
/** @internal
|
||||
* @brief Check whether the current process has read access to a file.
|
||||
*
|
||||
* @param[in] file Path to the file to check.
|
||||
*
|
||||
* @return 1 if the file is readable, 0 otherwise.
|
||||
*/
|
||||
int ssh_file_readaccess_ok(const char *file)
|
||||
{
|
||||
if (_access(file, 4) < 0) {
|
||||
@@ -207,6 +214,12 @@ struct tm *ssh_localtime(const time_t *timer, struct tm *result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the username of the currently running process.
|
||||
*
|
||||
* @return A newly allocated string with the username, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_local_username(void)
|
||||
{
|
||||
DWORD size = 0;
|
||||
@@ -233,6 +246,13 @@ char *ssh_get_local_username(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if the string is a valid IPv4 address, 0 otherwise.
|
||||
*/
|
||||
int ssh_is_ipaddr_v4(const char *str)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
@@ -256,6 +276,13 @@ int ssh_is_ipaddr_v4(const char *str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 or IPv6 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if valid IP address, 0 if not, -1 on memory error.
|
||||
*/
|
||||
int ssh_is_ipaddr(const char *str)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
@@ -298,7 +325,7 @@ int ssh_is_ipaddr(const char *str)
|
||||
#define NSS_BUFLEN_PASSWD 4096
|
||||
#endif /* NSS_BUFLEN_PASSWD */
|
||||
|
||||
char *ssh_get_user_home_dir(void)
|
||||
static char *ssh_get_user_home_dir_internal(void)
|
||||
{
|
||||
char *szPath = NULL;
|
||||
struct passwd pwd;
|
||||
@@ -313,7 +340,6 @@ char *ssh_get_user_home_dir(void)
|
||||
return NULL;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%s", szPath);
|
||||
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
@@ -322,7 +348,13 @@ char *ssh_get_user_home_dir(void)
|
||||
return szPath;
|
||||
}
|
||||
|
||||
/* we have read access on file */
|
||||
/** @internal
|
||||
* @brief Check whether the current process has read access to a file.
|
||||
*
|
||||
* @param[in] file Path to the file to check.
|
||||
*
|
||||
* @return 1 if the file is readable, 0 otherwise.
|
||||
*/
|
||||
int ssh_file_readaccess_ok(const char *file)
|
||||
{
|
||||
if (access(file, R_OK) < 0) {
|
||||
@@ -357,6 +389,12 @@ int ssh_dir_writeable(const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the username of the currently running process.
|
||||
*
|
||||
* @return A newly allocated string with the username, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_local_username(void)
|
||||
{
|
||||
struct passwd pwd;
|
||||
@@ -381,6 +419,13 @@ char *ssh_get_local_username(void)
|
||||
return name;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if the string is a valid IPv4 address, 0 otherwise.
|
||||
*/
|
||||
int ssh_is_ipaddr_v4(const char *str)
|
||||
{
|
||||
int rc = -1;
|
||||
@@ -394,6 +439,13 @@ int ssh_is_ipaddr_v4(const char *str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 or IPv6 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if valid IP address, 0 if not, -1 on memory error.
|
||||
*/
|
||||
int ssh_is_ipaddr(const char *str)
|
||||
{
|
||||
int rc = -1;
|
||||
@@ -428,6 +480,48 @@ int ssh_is_ipaddr(const char *str)
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/** @internal
|
||||
* @brief Get the home directory of the current user.
|
||||
*
|
||||
* If a session is provided and a cached value exists, it is returned directly.
|
||||
* Otherwise the home directory is looked up and cached in the session.
|
||||
*
|
||||
* @param[in] session The SSH session to cache the result in, or NULL.
|
||||
*
|
||||
* @return A newly allocated string with the home directory path, or NULL
|
||||
* on error. The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_user_home_dir(ssh_session session)
|
||||
{
|
||||
char *szPath = NULL;
|
||||
|
||||
/* If used previously, reuse cached value */
|
||||
if (session != NULL && session->opts.homedir != NULL) {
|
||||
return strdup(session->opts.homedir);
|
||||
}
|
||||
|
||||
szPath = ssh_get_user_home_dir_internal();
|
||||
if (szPath == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (session != NULL) {
|
||||
/* cache it:
|
||||
* failure is not fatal -- at worst we will just not cache it */
|
||||
session->opts.homedir = strdup(szPath);
|
||||
}
|
||||
|
||||
return szPath;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Convert a string to lowercase.
|
||||
*
|
||||
* @param[in] str The string to convert.
|
||||
*
|
||||
* @return A newly allocated lowercase copy of the string, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_lowercase(const char* str)
|
||||
{
|
||||
char *new = NULL, *p = NULL;
|
||||
@@ -448,6 +542,15 @@ char *ssh_lowercase(const char* str)
|
||||
return new;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Format a host and port into a "[host]:port" string.
|
||||
*
|
||||
* @param[in] host The hostname or IP address.
|
||||
* @param[in] port The port number.
|
||||
*
|
||||
* @return A newly allocated string of the form "[host]:port", or NULL
|
||||
* on error. The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_hostport(const char *host, int port)
|
||||
{
|
||||
char *dest = NULL;
|
||||
@@ -468,6 +571,38 @@ char *ssh_hostport(const char *host, int port)
|
||||
return dest;
|
||||
}
|
||||
|
||||
static char *
|
||||
ssh_get_hexa_internal(const unsigned char *what, size_t len, bool colons)
|
||||
{
|
||||
const char h[] = "0123456789abcdef";
|
||||
char *hexa = NULL;
|
||||
size_t i;
|
||||
size_t bytes_per_byte = 2 + (colons ? 1 : 0);
|
||||
size_t hlen = len * bytes_per_byte;
|
||||
|
||||
if (what == NULL || len < 1 || len > (UINT_MAX - 1) / bytes_per_byte) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hexa = calloc(hlen + 1, sizeof(char));
|
||||
if (hexa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hexa[i * bytes_per_byte] = h[(what[i] >> 4) & 0xF];
|
||||
hexa[i * bytes_per_byte + 1] = h[what[i] & 0xF];
|
||||
if (colons) {
|
||||
hexa[i * bytes_per_byte + 2] = ':';
|
||||
}
|
||||
}
|
||||
if (colons) {
|
||||
hexa[hlen - 1] = '\0';
|
||||
}
|
||||
|
||||
return hexa;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a buffer into a colon separated hex string.
|
||||
* The caller has to free the memory.
|
||||
@@ -483,28 +618,7 @@ char *ssh_hostport(const char *host, int port)
|
||||
*/
|
||||
char *ssh_get_hexa(const unsigned char *what, size_t len)
|
||||
{
|
||||
const char h[] = "0123456789abcdef";
|
||||
char *hexa = NULL;
|
||||
size_t i;
|
||||
size_t hlen = len * 3;
|
||||
|
||||
if (len > (UINT_MAX - 1) / 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hexa = malloc(hlen + 1);
|
||||
if (hexa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hexa[i * 3] = h[(what[i] >> 4) & 0xF];
|
||||
hexa[i * 3 + 1] = h[what[i] & 0xF];
|
||||
hexa[i * 3 + 2] = ':';
|
||||
}
|
||||
hexa[hlen - 1] = '\0';
|
||||
|
||||
return hexa;
|
||||
return ssh_get_hexa_internal(what, len, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -742,6 +856,11 @@ const char *ssh_version(int req_version)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Create a new empty linked list.
|
||||
*
|
||||
* @return A newly allocated ssh_list, or NULL on memory error.
|
||||
*/
|
||||
struct ssh_list *ssh_list_new(void)
|
||||
{
|
||||
struct ssh_list *ret = malloc(sizeof(struct ssh_list));
|
||||
@@ -752,6 +871,13 @@ struct ssh_list *ssh_list_new(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Free a linked list and all its iterator nodes.
|
||||
*
|
||||
* The data pointed to by each node is not freed.
|
||||
*
|
||||
* @param[in] list The list to free.
|
||||
*/
|
||||
void ssh_list_free(struct ssh_list *list)
|
||||
{
|
||||
struct ssh_iterator *ptr = NULL, *next = NULL;
|
||||
@@ -766,6 +892,13 @@ void ssh_list_free(struct ssh_list *list)
|
||||
SAFE_FREE(list);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the first iterator of a linked list.
|
||||
*
|
||||
* @param[in] list The list to iterate.
|
||||
*
|
||||
* @return Pointer to the first iterator, or NULL if the list is empty.
|
||||
*/
|
||||
struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
|
||||
{
|
||||
if (!list)
|
||||
@@ -773,6 +906,14 @@ struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
|
||||
return list->root;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Find the iterator pointing to a specific value in the list.
|
||||
*
|
||||
* @param[in] list The list to search.
|
||||
* @param[in] value The data pointer to find.
|
||||
*
|
||||
* @return The iterator pointing to the value, or NULL if not found.
|
||||
*/
|
||||
struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value)
|
||||
{
|
||||
struct ssh_iterator *it = NULL;
|
||||
@@ -848,6 +989,14 @@ int ssh_list_append(struct ssh_list *list, const void *data)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Prepend an element at the beginning of the list.
|
||||
*
|
||||
* @param[in] list The list to prepend to.
|
||||
* @param[in] data The element to prepend.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_list_prepend(struct ssh_list *list, const void *data)
|
||||
{
|
||||
struct ssh_iterator *it = NULL;
|
||||
@@ -873,6 +1022,12 @@ int ssh_list_prepend(struct ssh_list *list, const void *data)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Remove an element from the list by its iterator.
|
||||
*
|
||||
* @param[in] list The list to remove from.
|
||||
* @param[in] iterator The iterator pointing to the element to remove.
|
||||
*/
|
||||
void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator)
|
||||
{
|
||||
struct ssh_iterator *ptr = NULL, *prev = NULL;
|
||||
@@ -1189,7 +1344,7 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
} else {
|
||||
ld = strlen(d);
|
||||
p = (char *) d;
|
||||
h = ssh_get_user_home_dir();
|
||||
h = ssh_get_user_home_dir(NULL);
|
||||
}
|
||||
if (h == NULL) {
|
||||
return NULL;
|
||||
@@ -1211,15 +1366,125 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
return r;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the hostname of the local machine.
|
||||
*
|
||||
* @return A newly allocated string with the hostname, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_local_hostname(void)
|
||||
{
|
||||
char host[NI_MAXHOST] = {0};
|
||||
int rc;
|
||||
|
||||
rc = gethostname(host, sizeof(host));
|
||||
if (rc != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return strdup(host);
|
||||
}
|
||||
|
||||
static char *get_connection_hash(ssh_session session)
|
||||
{
|
||||
unsigned char conn_hash[SHA_DIGEST_LENGTH];
|
||||
char *local_hostname = NULL;
|
||||
SHACTX ctx = sha1_init();
|
||||
char strport[10] = {0};
|
||||
unsigned int port;
|
||||
char *username = NULL;
|
||||
int rc;
|
||||
|
||||
if (session == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ctx == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Local hostname %l */
|
||||
local_hostname = ssh_get_local_hostname();
|
||||
if (local_hostname == NULL) {
|
||||
goto err;
|
||||
}
|
||||
rc = sha1_update(ctx, local_hostname, strlen(local_hostname));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
SAFE_FREE(local_hostname);
|
||||
|
||||
/* Remote hostname %h */
|
||||
if (session->opts.host == NULL) {
|
||||
goto err;
|
||||
}
|
||||
rc = sha1_update(ctx, session->opts.host, strlen(session->opts.host));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Remote port %p */
|
||||
ssh_options_get_port(session, &port);
|
||||
snprintf(strport, sizeof(strport), "%d", port);
|
||||
rc = sha1_update(ctx, strport, strlen(strport));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* The remote username %r */
|
||||
username = session->opts.username;
|
||||
if (username == NULL) {
|
||||
/* fallback to local username: it will be used if not explicitly set */
|
||||
username = ssh_get_local_username();
|
||||
if (username == NULL) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
rc = sha1_update(ctx, username, strlen(username));
|
||||
if (username != session->opts.username) {
|
||||
free(username);
|
||||
}
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* ProxyJump */
|
||||
if (session->opts.proxy_jumps_str != NULL) {
|
||||
rc = sha1_update(ctx,
|
||||
session->opts.proxy_jumps_str,
|
||||
strlen(session->opts.proxy_jumps_str));
|
||||
}
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Frees context */
|
||||
rc = sha1_final(conn_hash, ctx);
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ssh_get_hexa_internal(conn_hash, SHA_DIGEST_LENGTH, false);
|
||||
|
||||
err:
|
||||
free(local_hostname);
|
||||
sha1_ctx_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief expands a string in function of session options
|
||||
*
|
||||
* @param[in] session The SSH session providing option values for expansion.
|
||||
* @param[in] s Format string to expand. Known parameters:
|
||||
* %d SSH configuration directory (~/.ssh)
|
||||
* %h target host name
|
||||
* %u local username
|
||||
* %l local hostname
|
||||
* %r remote username
|
||||
* %p remote port
|
||||
* - %d user home directory (~)
|
||||
* - %h target host name
|
||||
* - %u local username
|
||||
* - %l local hostname
|
||||
* - %r remote username
|
||||
* - %p remote port
|
||||
* - %j proxyjump string
|
||||
* - %C Hash of %l%h%p%r%j
|
||||
*
|
||||
* @returns Expanded string. The caller needs to free the memory using
|
||||
* ssh_string_free_char().
|
||||
*
|
||||
@@ -1227,7 +1492,6 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
*/
|
||||
char *ssh_path_expand_escape(ssh_session session, const char *s)
|
||||
{
|
||||
char host[NI_MAXHOST] = {0};
|
||||
char *buf = NULL;
|
||||
char *r = NULL;
|
||||
char *x = NULL;
|
||||
@@ -1276,65 +1540,69 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
|
||||
}
|
||||
|
||||
switch (*p) {
|
||||
case '%':
|
||||
goto escape;
|
||||
case 'd':
|
||||
if (session->opts.sshdir) {
|
||||
x = strdup(session->opts.sshdir);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Cannot expand sshdir");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
x = ssh_get_local_username();
|
||||
break;
|
||||
case 'l':
|
||||
if (gethostname(host, sizeof(host) == 0)) {
|
||||
x = strdup(host);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (session->opts.host) {
|
||||
x = strdup(session->opts.host);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Cannot expand host");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (session->opts.username) {
|
||||
x = strdup(session->opts.username);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Cannot expand username");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
char tmp[6];
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%hu",
|
||||
(uint16_t)(session->opts.port > 0 ? session->opts.port
|
||||
: 22));
|
||||
x = strdup(tmp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Wrong escape sequence detected");
|
||||
case '%':
|
||||
goto escape;
|
||||
case 'd':
|
||||
x = ssh_get_user_home_dir(session);
|
||||
if (x == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot expand homedir");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
x = ssh_get_local_username();
|
||||
break;
|
||||
case 'l':
|
||||
x = ssh_get_local_hostname();
|
||||
break;
|
||||
case 'h':
|
||||
if (session->opts.host) {
|
||||
x = strdup(session->opts.host);
|
||||
} else if (session->opts.originalhost) {
|
||||
x = strdup(session->opts.originalhost);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot expand host");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (session->opts.username) {
|
||||
x = strdup(session->opts.username);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot expand username");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'p': {
|
||||
char tmp[6];
|
||||
unsigned int port;
|
||||
|
||||
ssh_options_get_port(session, &port);
|
||||
snprintf(tmp, sizeof(tmp), "%u", port);
|
||||
x = strdup(tmp);
|
||||
break;
|
||||
}
|
||||
case 'j':
|
||||
if (session->opts.proxy_jumps_str != NULL) {
|
||||
x = strdup(session->opts.proxy_jumps_str);
|
||||
} else {
|
||||
x = strdup("");
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
x = get_connection_hash(session);
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Wrong escape sequence detected");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (x == NULL) {
|
||||
@@ -1346,8 +1614,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
|
||||
|
||||
i += strlen(x);
|
||||
if (i >= MAX_BUF_SIZE) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"String too long");
|
||||
ssh_set_error(session, SSH_FATAL, "String too long");
|
||||
free(buf);
|
||||
free(x);
|
||||
free(r);
|
||||
@@ -1640,6 +1907,15 @@ void burn_free(void *ptr, size_t len)
|
||||
}
|
||||
|
||||
#if !defined(HAVE_STRNDUP)
|
||||
|
||||
/** @internal
|
||||
* @brief Compatibility implementation of strndup for systems that lack it.
|
||||
*
|
||||
* @param[in] s The string to duplicate.
|
||||
* @param[in] n Maximum number of characters to copy.
|
||||
*
|
||||
* @return A newly allocated null-terminated string, or NULL on error.
|
||||
*/
|
||||
char *strndup(const char *s, size_t n)
|
||||
{
|
||||
char *x = NULL;
|
||||
@@ -1660,7 +1936,11 @@ char *strndup(const char *s, size_t n)
|
||||
}
|
||||
#endif /* ! HAVE_STRNDUP */
|
||||
|
||||
/* Increment 64b integer in network byte order */
|
||||
/** @internal
|
||||
* @brief Increment a 64-bit counter stored in network byte order.
|
||||
*
|
||||
* @param[in,out] counter Pointer to an 8-byte buffer holding the counter.
|
||||
*/
|
||||
void
|
||||
uint64_inc(unsigned char *counter)
|
||||
{
|
||||
@@ -2293,4 +2573,77 @@ ssh_libssh_proxy_jumps(void)
|
||||
return !(t != NULL && t[0] == '1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Safely open a file containing some configuration.
|
||||
*
|
||||
* Runs checks if the file can be used as some configuration file (is regular
|
||||
* file and is not too large). If so, returns the opened file (for reading).
|
||||
* Otherwise logs error and returns `NULL`.
|
||||
*
|
||||
* @param filename The path to the file to open.
|
||||
* @param max_file_size Maximum file size that is accepted.
|
||||
*
|
||||
* @returns the opened file or `NULL` on error.
|
||||
*/
|
||||
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
struct stat sb;
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
int r, fd;
|
||||
|
||||
/* open first to avoid TOCTOU */
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to open a file %s for reading: %s",
|
||||
filename,
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check the file is sensible for a configuration file */
|
||||
r = fstat(fd, &sb);
|
||||
if (r != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to stat %s: %s",
|
||||
filename,
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
if ((sb.st_mode & S_IFMT) != S_IFREG) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"The file %s is not a regular file: skipping",
|
||||
filename);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((size_t)sb.st_size > max_file_size) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"The file %s is too large (%jd MB > %zu MB): skipping",
|
||||
filename,
|
||||
(intmax_t)sb.st_size / 1024 / 1024,
|
||||
max_file_size / 1024 / 1024);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = fdopen(fd, "r");
|
||||
if (f == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Failed to open a file %s for reading: %s",
|
||||
filename,
|
||||
ssh_strerror(r, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* the flcose() will close also the underlying fd */
|
||||
return f;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
148
src/options.c
148
src/options.c
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -106,6 +107,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
}
|
||||
}
|
||||
|
||||
if (src->opts.originalhost != NULL) {
|
||||
new->opts.originalhost = strdup(src->opts.originalhost);
|
||||
if (new->opts.originalhost == NULL) {
|
||||
ssh_free(new);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (src->opts.bindaddr != NULL) {
|
||||
new->opts.bindaddr = strdup(src->opts.bindaddr);
|
||||
if (new->opts.bindaddr == NULL) {
|
||||
@@ -279,6 +288,20 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set a key exchange algorithm list option on the session.
|
||||
*
|
||||
* Supports prefix modifiers: '+' to append, '-' to remove, '^' to prepend
|
||||
* to the default algorithm list.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
* @param[in] algo The algorithm type to configure.
|
||||
* @param[in] list The algorithm list string.
|
||||
* @param[out] place Pointer to the string to store
|
||||
* the resulting algorithm list.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_options_set_algo(ssh_session session,
|
||||
enum ssh_kex_types_e algo,
|
||||
const char *list,
|
||||
@@ -369,8 +392,8 @@ int ssh_options_set_algo(ssh_session session,
|
||||
* default ssh directory.\n
|
||||
* \n
|
||||
* The ssh directory is used for files like known_hosts
|
||||
* and identity (private and public key). It may include
|
||||
* "%s" which will be replaced by the user home
|
||||
* and identity (private and public key). It may start
|
||||
* with ~ which will be replaced by the user home
|
||||
* directory.
|
||||
*
|
||||
* - SSH_OPTIONS_KNOWNHOSTS:
|
||||
@@ -718,18 +741,52 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
return -1;
|
||||
} else {
|
||||
char *username = NULL, *hostname = NULL;
|
||||
rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
|
||||
if (rc != SSH_OK) {
|
||||
char *strict_hostname = NULL;
|
||||
|
||||
/* Non-strict parse: reject shell metacharacters */
|
||||
rc = ssh_config_parse_uri(value,
|
||||
&username,
|
||||
&hostname,
|
||||
NULL,
|
||||
true,
|
||||
false);
|
||||
if (rc != SSH_OK || hostname == NULL) {
|
||||
SAFE_FREE(username);
|
||||
SAFE_FREE(hostname);
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Non-strict passed: set username and originalhost */
|
||||
if (username != NULL) {
|
||||
SAFE_FREE(session->opts.username);
|
||||
session->opts.username = username;
|
||||
}
|
||||
if (hostname != NULL) {
|
||||
if (!session->opts.config_hostname_only) {
|
||||
SAFE_FREE(session->opts.originalhost);
|
||||
session->opts.originalhost = hostname;
|
||||
} else {
|
||||
SAFE_FREE(hostname);
|
||||
}
|
||||
|
||||
/* Strict parse: set host only if valid hostname or IP */
|
||||
rc = ssh_config_parse_uri(value,
|
||||
NULL,
|
||||
&strict_hostname,
|
||||
NULL,
|
||||
true,
|
||||
true);
|
||||
if (rc != SSH_OK || strict_hostname == NULL) {
|
||||
SAFE_FREE(session->opts.host);
|
||||
session->opts.host = hostname;
|
||||
SAFE_FREE(strict_hostname);
|
||||
if (session->opts.config_hostname_only) {
|
||||
/* Config path: Hostname must be valid */
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
SAFE_FREE(session->opts.host);
|
||||
session->opts.host = strict_hostname;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -907,7 +964,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
SAFE_FREE(session->opts.global_knownhosts);
|
||||
if (v == NULL) {
|
||||
session->opts.global_knownhosts =
|
||||
strdup("/etc/ssh/ssh_known_hosts");
|
||||
strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
|
||||
if (session->opts.global_knownhosts == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
return -1;
|
||||
@@ -1197,7 +1254,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
} else {
|
||||
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||
rc = ssh_config_parse_proxy_jump(session, v, true);
|
||||
if (rc != SSH_OK) {
|
||||
return SSH_ERROR;
|
||||
@@ -1562,7 +1618,16 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
|
||||
* - SSH_OPTIONS_IDENTITY:
|
||||
* Get the first identity file name (const char *).\n
|
||||
* \n
|
||||
* By default id_rsa, id_ecdsa and id_ed25519 files are used.
|
||||
* By default `id_rsa`, `id_ecdsa`, `id_ed25519`, `id_ecdsa_sk`
|
||||
* and `id_ed25519_sk` (when SK support is built in) files are
|
||||
* used.
|
||||
*
|
||||
* - SSH_OPTIONS_NEXT_IDENTITY:
|
||||
* Get the next identity file name (const char *).\n
|
||||
* \n
|
||||
* Repeat calls to get all key paths. SSH_EOF is returned when
|
||||
* the end of list is reached. Another call will start another
|
||||
* iteration over the same list.
|
||||
*
|
||||
* - SSH_OPTIONS_PROXYCOMMAND:
|
||||
* Get the proxycommand necessary to log into the
|
||||
@@ -1638,7 +1703,8 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
|
||||
switch(type)
|
||||
{
|
||||
case SSH_OPTIONS_HOST:
|
||||
src = session->opts.host;
|
||||
src = session->opts.host ? session->opts.host
|
||||
: session->opts.originalhost;
|
||||
break;
|
||||
|
||||
case SSH_OPTIONS_USER:
|
||||
@@ -1658,6 +1724,30 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
|
||||
break;
|
||||
}
|
||||
|
||||
case SSH_OPTIONS_NEXT_IDENTITY: {
|
||||
if (session->opts.identity_it != NULL) {
|
||||
/* Move to the next item */
|
||||
session->opts.identity_it = session->opts.identity_it->next;
|
||||
if (session->opts.identity_it == NULL) {
|
||||
*value = NULL;
|
||||
return SSH_EOF;
|
||||
}
|
||||
} else {
|
||||
/* Get iterator from opts */
|
||||
struct ssh_iterator *it = NULL;
|
||||
it = ssh_list_get_iterator(session->opts.identity);
|
||||
if (it == NULL) {
|
||||
it = ssh_list_get_iterator(session->opts.identity_non_exp);
|
||||
}
|
||||
if (it == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
session->opts.identity_it = it;
|
||||
}
|
||||
src = ssh_iterator_value(char *, session->opts.identity_it);
|
||||
break;
|
||||
}
|
||||
|
||||
case SSH_OPTIONS_PROXYCOMMAND:
|
||||
src = session->opts.ProxyCommand;
|
||||
break;
|
||||
@@ -1948,7 +2038,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
|
||||
if (session == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (session->opts.host == NULL) {
|
||||
if (session->opts.originalhost == NULL) {
|
||||
ssh_set_error_invalid(session);
|
||||
return -1;
|
||||
}
|
||||
@@ -1963,7 +2053,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
|
||||
|
||||
/* set default filename */
|
||||
if (filename == NULL) {
|
||||
expanded_filename = ssh_path_expand_escape(session, "%d/config");
|
||||
expanded_filename = ssh_path_expand_escape(session, "%d/.ssh/config");
|
||||
} else {
|
||||
expanded_filename = ssh_path_expand_escape(session, filename);
|
||||
}
|
||||
@@ -1976,11 +2066,16 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
|
||||
goto out;
|
||||
}
|
||||
if (filename == NULL) {
|
||||
if ((fp = fopen(GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
|
||||
fp = ssh_strict_fopen(GLOBAL_CLIENT_CONFIG, SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (fp != NULL) {
|
||||
filename = GLOBAL_CLIENT_CONFIG;
|
||||
#ifdef USR_GLOBAL_CLIENT_CONFIG
|
||||
} else if ((fp = fopen(USR_GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
|
||||
filename = USR_GLOBAL_CLIENT_CONFIG;
|
||||
} else {
|
||||
fp = ssh_strict_fopen(USR_GLOBAL_CLIENT_CONFIG,
|
||||
SSH_MAX_CONFIG_FILE_SIZE);
|
||||
if (fp != NULL) {
|
||||
filename = USR_GLOBAL_CLIENT_CONFIG;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2000,6 +2095,16 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Apply default values for unset session options.
|
||||
*
|
||||
* Sets default SSH directory and username if not already configured,
|
||||
* and resolves any remaining option expansions.
|
||||
*
|
||||
* @param[in] session The SSH session to apply defaults to.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_options_apply(ssh_session session)
|
||||
{
|
||||
char *tmp = NULL;
|
||||
@@ -2021,7 +2126,7 @@ int ssh_options_apply(ssh_session session)
|
||||
|
||||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
|
||||
if (session->opts.knownhosts == NULL) {
|
||||
tmp = ssh_path_expand_escape(session, "%d/known_hosts");
|
||||
tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
|
||||
} else {
|
||||
tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
|
||||
}
|
||||
@@ -2035,7 +2140,7 @@ int ssh_options_apply(ssh_session session)
|
||||
|
||||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
|
||||
if (session->opts.global_knownhosts == NULL) {
|
||||
tmp = strdup("/etc/ssh/ssh_known_hosts");
|
||||
tmp = strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
|
||||
} else {
|
||||
tmp = ssh_path_expand_escape(session,
|
||||
session->opts.global_knownhosts);
|
||||
@@ -2364,6 +2469,15 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
|
||||
* not a pointer when it should have been a pointer, or if
|
||||
* its a pointer to a pointer when it should have just been
|
||||
* a pointer), then the behaviour is undefined.
|
||||
*
|
||||
* @warning Options set via this function may be overridden if a
|
||||
* configuration file is parsed afterwards (e.g., by an
|
||||
* implicit call to ssh_bind_options_parse_config() inside
|
||||
* ssh_bind_listen(), or by a manual call to the same
|
||||
* function) and contains the same options.\n
|
||||
* It is the caller’s responsibility to ensure the correct
|
||||
* order of API calls if explicit options must take
|
||||
* precedence.
|
||||
*/
|
||||
int
|
||||
ssh_bind_options_set(ssh_bind sshbind,
|
||||
|
||||
192
src/pki.c
192
src/pki.c
@@ -449,40 +449,137 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
|
||||
/* We should never reach this */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum ssh_digest_e ssh_key_hash_from_name(const char *name)
|
||||
/**
|
||||
* @brief Convert a signature algorithm name to a key type and hash type.
|
||||
*
|
||||
* Looks up the given signature algorithm name and returns both the
|
||||
* corresponding key type and digest algorithm in a single call,
|
||||
* avoiding double string comparisons on the same input.
|
||||
*
|
||||
* @param[in] name The signature algorithm name to convert (e.g.
|
||||
* "ssh-rsa", "rsa-sha2-256", "ecdsa-sha2-nistp256").
|
||||
*
|
||||
* @param[out] type A pointer to store the resulting key type.
|
||||
*
|
||||
* @param[out] hash_type A pointer to store the resulting hash/digest type.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if name is NULL or
|
||||
* unknown.
|
||||
*/
|
||||
int ssh_key_type_and_hash_from_signature_name(const char *name,
|
||||
enum ssh_keytypes_e *type,
|
||||
enum ssh_digest_e *hash_type)
|
||||
{
|
||||
if (name == NULL) {
|
||||
/* TODO we should rather fail */
|
||||
return SSH_DIGEST_AUTO;
|
||||
size_t len;
|
||||
|
||||
if (name == NULL || type == NULL || hash_type == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (strcmp(name, "ssh-rsa") == 0) {
|
||||
return SSH_DIGEST_SHA1;
|
||||
} else if (strcmp(name, "rsa-sha2-256") == 0) {
|
||||
return SSH_DIGEST_SHA256;
|
||||
} else if (strcmp(name, "rsa-sha2-512") == 0) {
|
||||
return SSH_DIGEST_SHA512;
|
||||
} else if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
|
||||
return SSH_DIGEST_SHA256;
|
||||
} else if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
|
||||
return SSH_DIGEST_SHA384;
|
||||
} else if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
|
||||
return SSH_DIGEST_SHA512;
|
||||
} else if (strcmp(name, "ssh-ed25519") == 0) {
|
||||
return SSH_DIGEST_AUTO;
|
||||
} else if (strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
|
||||
return SSH_DIGEST_SHA256;
|
||||
} else if (strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
|
||||
return SSH_DIGEST_AUTO;
|
||||
len = strlen(name);
|
||||
|
||||
if (len == 7 && strcmp(name, "ssh-rsa") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA;
|
||||
*hash_type = SSH_DIGEST_SHA1;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 11 && strcmp(name, "ssh-ed25519") == 0) {
|
||||
*type = SSH_KEYTYPE_ED25519;
|
||||
*hash_type = SSH_DIGEST_AUTO;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 12) {
|
||||
if (strcmp(name, "rsa-sha2-256") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "rsa-sha2-512") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 19) {
|
||||
if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P256;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P384;
|
||||
*hash_type = SSH_DIGEST_SHA384;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P521;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 26 && strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_SK_ED25519;
|
||||
*hash_type = SSH_DIGEST_AUTO;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 28 && strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA1;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 32 && strcmp(name, "ssh-ed25519-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ED25519_CERT01;
|
||||
*hash_type = SSH_DIGEST_AUTO;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 33) {
|
||||
if (strcmp(name, "rsa-sha2-256-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "rsa-sha2-512-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 34 && strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_SK_ECDSA;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 40) {
|
||||
if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P256_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P384_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA384;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P521_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unknown signature name %s", name);
|
||||
|
||||
/* TODO we should rather fail */
|
||||
return SSH_DIGEST_AUTO;
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks the given key against the configured allowed
|
||||
* public key algorithm types
|
||||
@@ -700,27 +797,6 @@ ssh_key_get_signature_algorithm(ssh_session session,
|
||||
return ssh_key_signature_to_char(type, hash_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a ssh key algorithm name to a ssh key algorithm type.
|
||||
*
|
||||
* @param[in] name The name to convert.
|
||||
*
|
||||
* @return The enum ssh key algorithm type.
|
||||
*/
|
||||
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name) {
|
||||
if (name == NULL) {
|
||||
return SSH_KEYTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
if ((strcmp(name, "rsa-sha2-256") == 0) ||
|
||||
(strcmp(name, "rsa-sha2-512") == 0)) {
|
||||
return SSH_KEYTYPE_RSA;
|
||||
}
|
||||
|
||||
/* Otherwise the key type matches the signature type */
|
||||
return ssh_key_type_from_name(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a ssh key name to a ssh key type.
|
||||
*
|
||||
@@ -834,6 +910,10 @@ int ssh_key_is_private(const ssh_key k) {
|
||||
/**
|
||||
* @brief Compare keys if they are equal.
|
||||
*
|
||||
* Note that comparing private keys is almost never needed. The private key
|
||||
* is cryptographically bound to the public key and comparing public keys should
|
||||
* always be preferred.
|
||||
*
|
||||
* @param[in] k1 The first key to compare.
|
||||
*
|
||||
* @param[in] k2 The second key to compare.
|
||||
@@ -2684,7 +2764,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
const char *filename)
|
||||
{
|
||||
char key_buf[MAX_LINE_SIZE];
|
||||
char host[256];
|
||||
char *host = NULL;
|
||||
char *b64_key = NULL;
|
||||
char *user = NULL;
|
||||
FILE *fp = NULL;
|
||||
@@ -2699,8 +2779,8 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = gethostname(host, sizeof(host));
|
||||
if (rc < 0) {
|
||||
host = ssh_get_local_hostname();
|
||||
if (host == NULL) {
|
||||
free(user);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
@@ -2708,6 +2788,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
|
||||
if (rc < 0) {
|
||||
free(user);
|
||||
free(host);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
@@ -2718,6 +2799,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
user,
|
||||
host);
|
||||
free(user);
|
||||
free(host);
|
||||
free(b64_key);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
@@ -2893,8 +2975,12 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
|
||||
}
|
||||
|
||||
alg = ssh_string_get_char(algorithm);
|
||||
type = ssh_key_type_from_signature_name(alg);
|
||||
hash_type = ssh_key_hash_from_name(alg);
|
||||
rc = ssh_key_type_and_hash_from_signature_name(alg, &type, &hash_type);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_BUFFER_FREE(buf);
|
||||
SSH_STRING_FREE(algorithm);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SSH_STRING_FREE(algorithm);
|
||||
|
||||
blob = ssh_buffer_get_ssh_string(buf);
|
||||
|
||||
@@ -110,7 +110,7 @@ void ssh_pki_ctx_free(ssh_pki_ctx context)
|
||||
* Set the RSA key size in bits for key generation.
|
||||
* Typically 2048, 3072, or 4096 bits. Must be greater
|
||||
* than or equal to 1024, as anything below is considered
|
||||
* insecure.
|
||||
* insecure. Use 0 (default) to use default key size (3072).
|
||||
*
|
||||
* - SSH_PKI_OPTION_SK_APPLICATION (const char *):
|
||||
* The Relying Party identifier (application string) that
|
||||
@@ -191,7 +191,7 @@ int ssh_pki_ctx_options_set(ssh_pki_ctx context,
|
||||
if (value == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARN, "RSA key size pointer must not be NULL");
|
||||
return SSH_ERROR;
|
||||
} else if (*(int *)value != 0 && *(int *)value <= RSA_MIN_KEY_SIZE) {
|
||||
} else if (*(int *)value != 0 && *(int *)value < RSA_MIN_KEY_SIZE) {
|
||||
SSH_LOG(
|
||||
SSH_LOG_WARN,
|
||||
"RSA key size must be greater than %d bits or 0 for default",
|
||||
|
||||
@@ -104,9 +104,9 @@ pki_ed25519_key_cmp(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
|
||||
}
|
||||
/* In the internal implementation, the private key is the concatenation
|
||||
* of the private seed with the public key. */
|
||||
cmp = memcmp(k1->ed25519_privkey,
|
||||
k2->ed25519_privkey,
|
||||
2 * ED25519_KEY_LEN);
|
||||
cmp = secure_memcmp(k1->ed25519_privkey,
|
||||
k2->ed25519_privkey,
|
||||
2 * ED25519_KEY_LEN);
|
||||
if (cmp != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
34
src/poll.c
34
src/poll.c
@@ -84,16 +84,33 @@ struct ssh_poll_ctx_struct {
|
||||
#ifdef HAVE_POLL
|
||||
#include <poll.h>
|
||||
|
||||
/** @internal
|
||||
* @brief Initialize the poll subsystem. No-op when native poll is available.
|
||||
*/
|
||||
void ssh_poll_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Clean up the poll subsystem. No-op when native poll is available.
|
||||
*/
|
||||
void ssh_poll_cleanup(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Wait for events on a set of file descriptors.
|
||||
*
|
||||
* @param fds Array of pollfd structures specifying the file descriptors.
|
||||
* @param nfds Number of file descriptors in the array.
|
||||
* @param timeout Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
|
||||
* to block indefinitely.
|
||||
*
|
||||
* @return Number of file descriptors with events, 0 on timeout,
|
||||
* -1 on error.
|
||||
*/
|
||||
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
|
||||
{
|
||||
return poll((struct pollfd *)fds, nfds, timeout);
|
||||
@@ -321,16 +338,33 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Initialize the poll subsystem using the BSD poll emulation.
|
||||
*/
|
||||
void ssh_poll_init(void)
|
||||
{
|
||||
ssh_poll_emu = bsd_poll;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Clean up the poll subsystem, resetting to the BSD poll emulation.
|
||||
*/
|
||||
void ssh_poll_cleanup(void)
|
||||
{
|
||||
ssh_poll_emu = bsd_poll;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Wait for events on a set of file descriptors.
|
||||
*
|
||||
* @param fds Array of pollfd structures specifying the file descriptors.
|
||||
* @param nfds Number of file descriptors in the array.
|
||||
* @param timeout Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
|
||||
* to block indefinitely.
|
||||
*
|
||||
* @return Number of file descriptors with events, 0 on timeout,
|
||||
* -1 on error.
|
||||
*/
|
||||
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
|
||||
{
|
||||
return (ssh_poll_emu)(fds, nfds, timeout);
|
||||
|
||||
16
src/scp.c
16
src/scp.c
@@ -874,6 +874,22 @@ int ssh_scp_pull_request(ssh_scp scp)
|
||||
size = strtoull(tmp, NULL, 10);
|
||||
p++;
|
||||
name = strdup(p);
|
||||
/* Catch invalid name:
|
||||
* - empty ones
|
||||
* - containing any forward slash -- directory traversal handled
|
||||
* differently
|
||||
* - special names "." and ".." referring to the current and parent
|
||||
* directories -- they are not expected either
|
||||
*/
|
||||
if (name == NULL || name[0] == '\0' || strchr(name, '/') ||
|
||||
strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
|
||||
ssh_set_error(scp->session,
|
||||
SSH_FATAL,
|
||||
"Received invalid filename: %s",
|
||||
name == NULL ? "<NULL>" : name);
|
||||
SAFE_FREE(name);
|
||||
goto error;
|
||||
}
|
||||
SAFE_FREE(scp->request_name);
|
||||
scp->request_name = name;
|
||||
if (buffer[0] == 'C') {
|
||||
|
||||
@@ -168,7 +168,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
#endif /* WITH_GSSAPI */
|
||||
|
||||
id = strdup("%d/id_ed25519");
|
||||
id = strdup("%d/.ssh/id_ed25519");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -179,7 +179,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
id = strdup("%d/id_ecdsa");
|
||||
id = strdup("%d/.ssh/id_ecdsa");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
id = strdup("%d/id_rsa");
|
||||
id = strdup("%d/.ssh/id_rsa");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -200,7 +200,7 @@ ssh_session ssh_new(void)
|
||||
|
||||
#ifdef WITH_FIDO2
|
||||
/* Add security key identities */
|
||||
id = strdup("%d/id_ed25519_sk");
|
||||
id = strdup("%d/.ssh/id_ed25519_sk");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
id = strdup("%d/id_ecdsa_sk");
|
||||
id = strdup("%d/.ssh/id_ecdsa_sk");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -384,6 +384,7 @@ void ssh_free(ssh_session session)
|
||||
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||
SSH_LIST_FREE(session->opts.proxy_jumps);
|
||||
SSH_LIST_FREE(session->opts.proxy_jumps_user_cb);
|
||||
SAFE_FREE(session->opts.proxy_jumps_str);
|
||||
|
||||
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
|
||||
session->out_queue)) != NULL) {
|
||||
@@ -405,6 +406,8 @@ void ssh_free(ssh_session session)
|
||||
SAFE_FREE(session->opts.bindaddr);
|
||||
SAFE_FREE(session->opts.username);
|
||||
SAFE_FREE(session->opts.host);
|
||||
SAFE_FREE(session->opts.originalhost);
|
||||
SAFE_FREE(session->opts.homedir);
|
||||
SAFE_FREE(session->opts.sshdir);
|
||||
SAFE_FREE(session->opts.knownhosts);
|
||||
SAFE_FREE(session->opts.global_knownhosts);
|
||||
@@ -1007,7 +1010,9 @@ int ssh_get_version(ssh_session session) {
|
||||
/**
|
||||
* @internal
|
||||
* @brief Callback to be called when the socket received an exception code.
|
||||
* @param user is a pointer to session
|
||||
* @param code The exception code from the socket layer.
|
||||
* @param errno_code The errno value associated with the exception.
|
||||
* @param user Pointer to the SSH session.
|
||||
*/
|
||||
void ssh_socket_exception_callback(int code, int errno_code, void *user){
|
||||
ssh_session session = (ssh_session)user;
|
||||
|
||||
65
src/sftp.c
65
src/sftp.c
@@ -583,44 +583,51 @@ int sftp_init(sftp_session sftp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int sftp_extensions_get_count(sftp_session sftp) {
|
||||
if (sftp == NULL || sftp->ext == NULL) {
|
||||
return 0;
|
||||
}
|
||||
unsigned int sftp_extensions_get_count(sftp_session sftp)
|
||||
{
|
||||
if (sftp == NULL || sftp->ext == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sftp->ext->count;
|
||||
return sftp->ext->count;
|
||||
}
|
||||
|
||||
const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) {
|
||||
if (sftp == NULL)
|
||||
return NULL;
|
||||
if (sftp->ext == NULL || sftp->ext->name == NULL) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx)
|
||||
{
|
||||
if (sftp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (idx > sftp->ext->count) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
if (sftp->ext == NULL || sftp->ext->name == NULL) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sftp->ext->name[idx];
|
||||
if (idx >= sftp->ext->count) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sftp->ext->name[idx];
|
||||
}
|
||||
|
||||
const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) {
|
||||
if (sftp == NULL)
|
||||
return NULL;
|
||||
if (sftp->ext == NULL || sftp->ext->name == NULL) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx)
|
||||
{
|
||||
if (sftp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (idx > sftp->ext->count) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
if (sftp->ext == NULL || sftp->ext->name == NULL) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sftp->ext->data[idx];
|
||||
if (idx >= sftp->ext->count) {
|
||||
ssh_set_error_invalid(sftp->session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sftp->ext->data[idx];
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -461,19 +461,24 @@ enum sftp_longname_field_e {
|
||||
static char * sftp_parse_longname(const char *longname,
|
||||
enum sftp_longname_field_e longname_field)
|
||||
{
|
||||
const char *p, *q;
|
||||
const char *p = NULL, *q = NULL;
|
||||
size_t len, field = 0;
|
||||
|
||||
if (longname == NULL || longname_field < SFTP_LONGNAME_PERM ||
|
||||
longname_field > SFTP_LONGNAME_NAME) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = longname;
|
||||
/*
|
||||
* Find the beginning of the field which is specified
|
||||
* by sftp_longname_field_e.
|
||||
*/
|
||||
while (field != longname_field) {
|
||||
while (*p != '\0' && field != longname_field) {
|
||||
if (isspace(*p)) {
|
||||
field++;
|
||||
p++;
|
||||
while (*p && isspace(*p)) {
|
||||
while (*p != '\0' && isspace(*p)) {
|
||||
p++;
|
||||
}
|
||||
} else {
|
||||
@@ -481,8 +486,13 @@ static char * sftp_parse_longname(const char *longname,
|
||||
}
|
||||
}
|
||||
|
||||
/* If we reached NULL before we got our field fail */
|
||||
if (field != longname_field) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
q = p;
|
||||
while (! isspace(*q)) {
|
||||
while (*q != '\0' && !isspace(*q)) {
|
||||
q++;
|
||||
}
|
||||
|
||||
|
||||
485
src/sftpserver.c
485
src/sftpserver.c
@@ -24,11 +24,12 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <grp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#endif
|
||||
|
||||
@@ -239,9 +240,7 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
||||
}
|
||||
break;
|
||||
case SSH_FXP_EXTENDED:
|
||||
rc = ssh_buffer_unpack(payload,
|
||||
"s",
|
||||
&msg->submessage);
|
||||
rc = ssh_buffer_unpack(payload, "s", &msg->submessage);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
@@ -256,9 +255,13 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
||||
goto error;
|
||||
}
|
||||
} else if (strcmp(msg->submessage, "statvfs@openssh.com") == 0 ){
|
||||
rc = ssh_buffer_unpack(payload,
|
||||
"s",
|
||||
&msg->filename);
|
||||
rc = ssh_buffer_unpack(payload, "s", &msg->filename);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
} else if (strcmp(msg->submessage,
|
||||
"users-groups-by-id@openssh.com") == 0) {
|
||||
rc = ssh_buffer_unpack(payload, "SS", &msg->data, &msg->handle);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
@@ -489,7 +492,10 @@ sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending name %s", ssh_string_get_char(file));
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Sending name %s, ID %" PRIu32,
|
||||
ssh_string_get_char(file),
|
||||
msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
ssh_buffer_add_u32(out, htonl(1)) < 0 ||
|
||||
@@ -532,6 +538,7 @@ int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
|
||||
ssh_log_hexdump("Sending handle:",
|
||||
(const unsigned char *)ssh_string_get_char(handle),
|
||||
ssh_string_len(handle));
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "packet ID %" PRIu32, msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
ssh_buffer_add_ssh_string(out, handle) < 0 ||
|
||||
@@ -565,7 +572,7 @@ int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending attr");
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending attr, ID %" PRIu32, msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
buffer_add_attributes(out, attr) < 0 ||
|
||||
@@ -653,7 +660,10 @@ int sftp_reply_names(sftp_client_message msg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d names", msg->attr_num);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Sending %d names, ID %" PRIu32,
|
||||
msg->attr_num,
|
||||
msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
|
||||
@@ -705,8 +715,11 @@ sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending status %d, message: %s", status,
|
||||
ssh_string_get_char(s));
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Sending status %d, message: %s, ID %" PRIu32,
|
||||
status,
|
||||
ssh_string_get_char(s),
|
||||
msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
ssh_buffer_add_u32(out, htonl(status)) < 0 ||
|
||||
@@ -745,7 +758,10 @@ int sftp_reply_data(sftp_client_message msg, const void *data, int len)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending data, length: %d", len);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Sending data, length: %d, ID %" PRIu32,
|
||||
len,
|
||||
msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
ssh_buffer_add_u32(out, ntohl(len)) < 0 ||
|
||||
@@ -780,7 +796,7 @@ sftp_reply_statvfs(sftp_client_message msg, sftp_statvfs_t st)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending statvfs reply");
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending statvfs reply, ID %" PRIu32, msg->id);
|
||||
|
||||
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
|
||||
ssh_buffer_add_u64(out, ntohll(st->f_bsize)) < 0 ||
|
||||
@@ -810,7 +826,9 @@ int sftp_reply_version(sftp_client_message client_msg)
|
||||
ssh_buffer reply;
|
||||
int rc;
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Sending version packet");
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Sending version packet, ID %" PRIu32,
|
||||
client_msg->id);
|
||||
|
||||
version = sftp->client_version;
|
||||
reply = ssh_buffer_new();
|
||||
@@ -819,14 +837,17 @@ int sftp_reply_version(sftp_client_message client_msg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(reply, "dssssss",
|
||||
rc = ssh_buffer_pack(reply,
|
||||
"dssssssss",
|
||||
LIBSFTP_VERSION,
|
||||
"posix-rename@openssh.com",
|
||||
"1",
|
||||
"hardlink@openssh.com",
|
||||
"1",
|
||||
"statvfs@openssh.com",
|
||||
"2");
|
||||
"2",
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
SSH_BUFFER_FREE(reply);
|
||||
@@ -933,6 +954,14 @@ void *sftp_handle(sftp_session sftp, ssh_string handle)
|
||||
return sftp->handles[val];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove an SFTP file or directory handle from the session.
|
||||
*
|
||||
* @param sftp The SFTP session.
|
||||
* @param handle The handle to remove.
|
||||
*
|
||||
* @see sftp_handle_alloc()
|
||||
*/
|
||||
void sftp_handle_remove(sftp_session sftp, void *handle)
|
||||
{
|
||||
int i;
|
||||
@@ -1044,6 +1073,352 @@ struct sftp_handle
|
||||
char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Parse a blob of IDs into a sftp_name_id_map.
|
||||
*
|
||||
* This function extracts numeric IDs from the binary blob and populates
|
||||
* the 'ids' array of the map. Note that each element of the 'names'
|
||||
* array in the map is initialized to NULL by this function.
|
||||
*
|
||||
* @param[in] ids_blob The binary string blob containing uint32_t IDs.
|
||||
*
|
||||
* @return A newly allocated sftp_name_id_map on success, or NULL on error.
|
||||
*/
|
||||
static sftp_name_id_map sftp_name_id_map_from_ids_blob(ssh_string ids_blob)
|
||||
{
|
||||
sftp_name_id_map map = NULL;
|
||||
ssh_buffer buf = NULL;
|
||||
size_t len;
|
||||
uint32_t count, i;
|
||||
int rc;
|
||||
|
||||
if (ids_blob == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "IDs blob is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = ssh_string_len(ids_blob);
|
||||
|
||||
if (len % sizeof(uint32_t) != 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"IDs blob length is not a multiple of 4 bytes");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = len / sizeof(uint32_t);
|
||||
map = sftp_name_id_map_new(count);
|
||||
if (map == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate sftp_name_id_map");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = ssh_buffer_new();
|
||||
if (buf == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate ssh_buffer");
|
||||
sftp_name_id_map_free(map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_data(buf, ssh_string_data(ids_blob), len);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to copy blob data to buffer");
|
||||
SSH_BUFFER_FREE(buf);
|
||||
sftp_name_id_map_free(map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uint32_t val;
|
||||
rc = ssh_buffer_unpack(buf, "d", &val);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to unpack ID from buffer");
|
||||
SSH_BUFFER_FREE(buf);
|
||||
sftp_name_id_map_free(map);
|
||||
return NULL;
|
||||
}
|
||||
map->ids[i] = val;
|
||||
}
|
||||
|
||||
SSH_BUFFER_FREE(buf);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Fill the names array using UIDs in the map.
|
||||
*/
|
||||
static int sftp_fill_names_using_uids(sftp_name_id_map users_map)
|
||||
{
|
||||
struct passwd pwd_struct;
|
||||
struct passwd *pwd_res = NULL;
|
||||
long pwd_buf_size = -1;
|
||||
char *pwd_lookup_buf = NULL;
|
||||
uint32_t i;
|
||||
int rc = SSH_OK;
|
||||
|
||||
if (users_map == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#ifdef _SC_GETPW_R_SIZE_MAX
|
||||
pwd_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
#endif
|
||||
if (pwd_buf_size <= 0) {
|
||||
pwd_buf_size = 16384;
|
||||
}
|
||||
pwd_lookup_buf = calloc(1, pwd_buf_size);
|
||||
if (pwd_lookup_buf == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate pwd lookup buffer");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < users_map->count; i++) {
|
||||
int ret = getpwuid_r(users_map->ids[i],
|
||||
&pwd_struct,
|
||||
pwd_lookup_buf,
|
||||
pwd_buf_size,
|
||||
&pwd_res);
|
||||
|
||||
SAFE_FREE(users_map->names[i]);
|
||||
|
||||
if (ret == 0 && pwd_res != NULL) {
|
||||
users_map->names[i] = strdup(pwd_res->pw_name);
|
||||
} else {
|
||||
users_map->names[i] = strdup("");
|
||||
}
|
||||
|
||||
if (users_map->names[i] == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to allocate memory for username string");
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_FREE(pwd_lookup_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Fill the names array using GIDs in the map.
|
||||
*/
|
||||
static int sftp_fill_names_using_gids(sftp_name_id_map groups_map)
|
||||
{
|
||||
struct group grp_struct;
|
||||
struct group *grp_res = NULL;
|
||||
long grp_buf_size = -1;
|
||||
char *grp_lookup_buf = NULL;
|
||||
uint32_t i;
|
||||
int rc = SSH_OK;
|
||||
|
||||
if (groups_map == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#ifdef _SC_GETGR_R_SIZE_MAX
|
||||
grp_buf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
#endif
|
||||
if (grp_buf_size <= 0) {
|
||||
grp_buf_size = 16384;
|
||||
}
|
||||
grp_lookup_buf = calloc(1, grp_buf_size);
|
||||
if (grp_lookup_buf == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate grp lookup buffer");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < groups_map->count; i++) {
|
||||
int ret = getgrgid_r(groups_map->ids[i],
|
||||
&grp_struct,
|
||||
grp_lookup_buf,
|
||||
grp_buf_size,
|
||||
&grp_res);
|
||||
|
||||
SAFE_FREE(groups_map->names[i]);
|
||||
|
||||
if (ret == 0 && grp_res != NULL) {
|
||||
groups_map->names[i] = strdup(grp_res->gr_name);
|
||||
} else {
|
||||
groups_map->names[i] = strdup("");
|
||||
}
|
||||
|
||||
if (groups_map->names[i] == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to allocate memory for group name string");
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_FREE(grp_lookup_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Pack the resolved names from a map into the output buffer.
|
||||
*
|
||||
* This function formats the multiple names according to the
|
||||
* users-groups-by-id@openssh.com extension specification. Each name in
|
||||
* the map is individually encoded as an SSH string. The entire concatenated
|
||||
* sequence of these encoded names is then wrapped and appended to the
|
||||
* output buffer as one single, large SSH string.
|
||||
*
|
||||
* @param[out] out_buffer The destination buffer for the final packed string.
|
||||
* @param[in] map The map containing the resolved names.
|
||||
*
|
||||
* @return SSH_OK on success, or SSH_ERROR on memory allocation failure.
|
||||
*/
|
||||
static int sftp_buffer_add_names(ssh_buffer out_buffer, sftp_name_id_map map)
|
||||
{
|
||||
ssh_buffer temp_buffer = NULL;
|
||||
uint32_t i;
|
||||
int rc;
|
||||
|
||||
if (out_buffer == NULL || map == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
temp_buffer = ssh_buffer_new();
|
||||
if (temp_buffer == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to allocate temporary buffer for names");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->count; i++) {
|
||||
const char *name = map->names[i] != NULL ? map->names[i] : "";
|
||||
rc = ssh_buffer_pack(temp_buffer, "s", name);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to pack name into buffer");
|
||||
SSH_BUFFER_FREE(temp_buffer);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(out_buffer,
|
||||
"dP",
|
||||
(uint32_t)ssh_buffer_get_len(temp_buffer),
|
||||
(size_t)ssh_buffer_get_len(temp_buffer),
|
||||
ssh_buffer_get(temp_buffer));
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to add names string blob to output buffer");
|
||||
}
|
||||
|
||||
SSH_BUFFER_FREE(temp_buffer);
|
||||
return (rc != SSH_OK) ? SSH_ERROR : SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Handle users-groups-by-id@openssh.com extension request.
|
||||
*
|
||||
* Resolves numeric user IDs (UIDs) and group IDs (GIDs) to their
|
||||
* corresponding username and group name strings. Returns empty strings
|
||||
* for IDs that cannot be resolved.
|
||||
*
|
||||
* @param[in] client_msg The SFTP client message containing the request.
|
||||
* client_msg->data contains the UIDs blob.
|
||||
* client_msg->handle contains the GIDs blob.
|
||||
*
|
||||
* @return SSH_OK on success (reply sent to client). On error, an error
|
||||
* status is sent to the client and SSH_ERROR is returned.
|
||||
*/
|
||||
static int process_users_groups_by_id(sftp_client_message client_msg)
|
||||
{
|
||||
ssh_buffer out = NULL;
|
||||
sftp_name_id_map users_map = NULL;
|
||||
sftp_name_id_map groups_map = NULL;
|
||||
int rc;
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Processing users-groups-by-id extension");
|
||||
|
||||
if (client_msg->data == NULL || client_msg->handle == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing UIDs or GIDs blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
users_map = sftp_name_id_map_from_ids_blob(client_msg->data);
|
||||
if (users_map == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to parse UIDs blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
groups_map = sftp_name_id_map_from_ids_blob(client_msg->handle);
|
||||
if (groups_map == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to parse GIDs blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_fill_names_using_uids(users_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to resolve UIDs");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_fill_names_using_gids(groups_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to resolve GIDs");
|
||||
goto error;
|
||||
}
|
||||
|
||||
out = ssh_buffer_new();
|
||||
if (out == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate output buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_u32(out, client_msg->id);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to add request ID to buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_buffer_add_names(out, users_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to add users to buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_buffer_add_names(out, groups_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to add groups to buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_packet_write(client_msg->sftp, SSH_FXP_EXTENDED_REPLY, out);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to send extended reply");
|
||||
goto error;
|
||||
}
|
||||
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
SSH_BUFFER_FREE(out);
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Successfully processed request");
|
||||
return SSH_OK;
|
||||
|
||||
error:
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
SSH_BUFFER_FREE(out);
|
||||
SSH_LOG(SSH_LOG_WARNING, "Sending error response");
|
||||
|
||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Internal processing error");
|
||||
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
SSH_SFTP_CALLBACK(process_unsupported);
|
||||
SSH_SFTP_CALLBACK(process_open);
|
||||
SSH_SFTP_CALLBACK(process_read);
|
||||
@@ -1088,6 +1463,10 @@ const struct sftp_message_handler message_handlers[] = {
|
||||
const struct sftp_message_handler extended_handlers[] = {
|
||||
/* here are some extended type handlers */
|
||||
{"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
|
||||
{"users-groups-by-id",
|
||||
"users-groups-by-id@openssh.com",
|
||||
0,
|
||||
process_users_groups_by_id},
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
@@ -1183,6 +1562,10 @@ process_read(sftp_client_message client_msg)
|
||||
ssh_log_hexdump("Processing read: handle:",
|
||||
(const unsigned char *)ssh_string_get_char(handle),
|
||||
ssh_string_len(handle));
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Offset %" PRIu64", length %" PRIu32,
|
||||
client_msg->offset,
|
||||
client_msg->len);
|
||||
|
||||
h = sftp_handle(sftp, handle);
|
||||
if (h != NULL && h->type == SFTP_FILE_HANDLE) {
|
||||
@@ -1241,6 +1624,10 @@ process_write(sftp_client_message client_msg)
|
||||
ssh_log_hexdump("Processing write: handle",
|
||||
(const unsigned char *)ssh_string_get_char(handle),
|
||||
ssh_string_len(handle));
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"Offset %" PRIu64", length %" PRIu32,
|
||||
client_msg->offset,
|
||||
client_msg->len);
|
||||
|
||||
h = sftp_handle(sftp, handle);
|
||||
if (h != NULL && h->type == SFTP_FILE_HANDLE) {
|
||||
@@ -1343,6 +1730,15 @@ process_opendir(sftp_client_message client_msg)
|
||||
}
|
||||
h->dirp = dir;
|
||||
h->name = strdup(dir_name);
|
||||
if (h->name == NULL) {
|
||||
free(h);
|
||||
closedir(dir);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "failed to duplicate directory name");
|
||||
sftp_reply_status(client_msg,
|
||||
SSH_FX_FAILURE,
|
||||
"Failed to allocate new handle");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
h->type = SFTP_DIR_HANDLE;
|
||||
handle_s = sftp_handle_alloc(client_msg->sftp, h);
|
||||
|
||||
@@ -1350,6 +1746,7 @@ process_opendir(sftp_client_message client_msg)
|
||||
sftp_reply_handle(client_msg, handle_s);
|
||||
ssh_string_free(handle_s);
|
||||
} else {
|
||||
SAFE_FREE(h->name);
|
||||
free(h);
|
||||
closedir(dir);
|
||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
|
||||
@@ -1958,7 +2355,10 @@ dispatch_sftp_request(sftp_client_message sftp_msg)
|
||||
sftp_server_message_callback handler = NULL;
|
||||
uint8_t type = sftp_client_message_get_type(sftp_msg);
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "processing request type: %u", type);
|
||||
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||
"processing request type: %" PRIu8 ", ID %" PRIu32,
|
||||
type,
|
||||
sftp_msg->id);
|
||||
|
||||
for (int i = 0; message_handlers[i].cb != NULL; i++) {
|
||||
if (type == message_handlers[i].type) {
|
||||
@@ -2035,16 +2435,19 @@ sftp_channel_default_subsystem_request(ssh_session session,
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default data callback for sftp server
|
||||
* @brief Default channel data callback for an SFTP server subsystem.
|
||||
*
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] channel The ssh channel with SFTP session opened
|
||||
* @param[in] data The data to be processed.
|
||||
* @param[in] len The length of input data to be processed
|
||||
* @param[in] is_stderr Unused channel flag for stderr flagging
|
||||
* @param[in] userdata The pointer to sftp_session
|
||||
* Processes incoming data on the channel and dispatches it to the SFTP
|
||||
* server message handler.
|
||||
*
|
||||
* @return number of bytes processed, -1 when error occurs.
|
||||
* @param[in] session The SSH session.
|
||||
* @param[in] channel The SSH channel carrying the SFTP data.
|
||||
* @param[in] data The received data buffer.
|
||||
* @param[in] len The length of the data buffer.
|
||||
* @param[in] is_stderr Unused; SFTP does not use the stderr stream.
|
||||
* @param[in] userdata Pointer to the sftp_session handle.
|
||||
*
|
||||
* @return Number of bytes processed on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int
|
||||
sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||
@@ -2057,7 +2460,7 @@ sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||
sftp_session *sftpp = (sftp_session *)userdata;
|
||||
sftp_session sftp = NULL;
|
||||
sftp_client_message msg;
|
||||
int decode_len;
|
||||
uint32_t undecoded_len = len;
|
||||
int rc;
|
||||
|
||||
if (sftpp == NULL) {
|
||||
@@ -2066,17 +2469,25 @@ sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||
}
|
||||
sftp = *sftpp;
|
||||
|
||||
decode_len = sftp_decode_channel_data_to_packet(sftp, data, len);
|
||||
if (decode_len == SSH_ERROR)
|
||||
return SSH_ERROR;
|
||||
do {
|
||||
int decode_len =
|
||||
sftp_decode_channel_data_to_packet(sftp, data, undecoded_len);
|
||||
if (decode_len == SSH_ERROR) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
msg = sftp_get_client_message_from_packet(sftp);
|
||||
rc = process_client_message(msg);
|
||||
sftp_client_message_free(msg);
|
||||
if (rc != SSH_OK)
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
|
||||
msg = sftp_get_client_message_from_packet(sftp);
|
||||
rc = process_client_message(msg);
|
||||
sftp_client_message_free(msg);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
|
||||
}
|
||||
|
||||
return decode_len;
|
||||
undecoded_len -= decode_len;
|
||||
data = (uint8_t *)data + decode_len;
|
||||
} while (undecoded_len > 0);
|
||||
|
||||
return len;
|
||||
}
|
||||
#else
|
||||
/* Not available on Windows for now */
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "libssh/sntrup761.h"
|
||||
#ifdef HAVE_SNTRUP761
|
||||
|
||||
#include "libssh/bignum.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include "libssh/dh.h"
|
||||
@@ -141,7 +140,7 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
|
||||
{
|
||||
unsigned char ssk[SNTRUP761_SIZE + CURVE25519_PUBKEY_SIZE];
|
||||
unsigned char *k = ssk + SNTRUP761_SIZE;
|
||||
unsigned char hss[SHA512_DIGEST_LEN];
|
||||
void *shared_secret_data = NULL;
|
||||
int rc;
|
||||
|
||||
rc = ssh_curve25519_create_k(session, k);
|
||||
@@ -216,22 +215,27 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
|
||||
ssh_log_hexdump("kem key", ssk, SNTRUP761_SIZE);
|
||||
#endif
|
||||
|
||||
sha512(ssk, sizeof ssk, hss);
|
||||
|
||||
bignum_bin2bn(hss, sizeof hss, &session->next_crypto->shared_secret);
|
||||
if (session->next_crypto->shared_secret == NULL) {
|
||||
ssh_string_burn(session->next_crypto->hybrid_shared_secret);
|
||||
ssh_string_free(session->next_crypto->hybrid_shared_secret);
|
||||
session->next_crypto->hybrid_shared_secret =
|
||||
ssh_string_new(SHA512_DIGEST_LEN);
|
||||
if (session->next_crypto->hybrid_shared_secret == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
rc = SSH_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
shared_secret_data =
|
||||
ssh_string_data(session->next_crypto->hybrid_shared_secret);
|
||||
|
||||
sha512(ssk, sizeof ssk, shared_secret_data);
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret);
|
||||
ssh_log_hexdump("Shared secret key", shared_secret_data, SHA512_DIGEST_LEN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
cleanup:
|
||||
ssh_burn(ssk, sizeof ssk);
|
||||
ssh_burn(hss, sizeof hss);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
38
src/socket.c
38
src/socket.c
@@ -65,6 +65,9 @@ struct sockaddr_un {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @internal
|
||||
* @brief Represents the possible states of an SSH socket connection.
|
||||
*/
|
||||
enum ssh_socket_states_e {
|
||||
SSH_SOCKET_NONE,
|
||||
SSH_SOCKET_CONNECTING,
|
||||
@@ -444,7 +447,8 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
|
||||
/** @internal
|
||||
* @brief returns the poll handle corresponding to the socket,
|
||||
* creates it if it does not exist.
|
||||
* @returns allocated and initialized ssh_poll_handle object
|
||||
*
|
||||
* @return allocated and initialized ssh_poll_handle object
|
||||
*/
|
||||
ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
|
||||
{
|
||||
@@ -1062,12 +1066,26 @@ int ssh_socket_get_poll_flags(ssh_socket s)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to non-blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_socket_set_nonblocking(socket_t fd)
|
||||
{
|
||||
u_long nonblocking = 1;
|
||||
return ioctlsocket(fd, FIONBIO, &nonblocking);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_socket_set_blocking(socket_t fd)
|
||||
{
|
||||
u_long nonblocking = 0;
|
||||
@@ -1075,11 +1093,25 @@ int ssh_socket_set_blocking(socket_t fd)
|
||||
}
|
||||
|
||||
#else /* _WIN32 */
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to non-blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_socket_set_nonblocking(socket_t fd)
|
||||
{
|
||||
return fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int ssh_socket_set_blocking(socket_t fd)
|
||||
{
|
||||
return fcntl(fd, F_SETFL, 0);
|
||||
@@ -1435,7 +1467,7 @@ ssh_socket_connect_proxyjump(ssh_socket s)
|
||||
|
||||
session = s->session;
|
||||
|
||||
SSH_LOG(SSH_LOG_INFO,
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Connecting to host %s port %d user %s through ProxyJump",
|
||||
session->opts.host,
|
||||
session->opts.port,
|
||||
@@ -1515,7 +1547,7 @@ ssh_socket_connect_proxyjump(ssh_socket s)
|
||||
/* transferred to the jump_thread_data */
|
||||
jump_session = NULL;
|
||||
|
||||
SSH_LOG(SSH_LOG_INFO,
|
||||
SSH_LOG(SSH_LOG_DEBUG,
|
||||
"Starting proxy thread to host %s port %d user %s, callbacks=%p",
|
||||
jump_thread_data->next_jump->hostname,
|
||||
jump_thread_data->next_jump->port,
|
||||
|
||||
@@ -38,7 +38,6 @@ static struct ssh_threads_callbacks_struct *user_callbacks = NULL;
|
||||
/** @internal
|
||||
* @brief inits the threading with the backend cryptographic libraries
|
||||
*/
|
||||
|
||||
int ssh_threads_init(void)
|
||||
{
|
||||
static int threads_initialized = 0;
|
||||
@@ -63,6 +62,9 @@ int ssh_threads_init(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Finalize and clean up the threading backend of the crypto libraries.
|
||||
*/
|
||||
void ssh_threads_finalize(void)
|
||||
{
|
||||
crypto_thread_finalize();
|
||||
@@ -84,6 +86,12 @@ int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the type identifier of the currently active threading callbacks.
|
||||
*
|
||||
* @return A string identifying the threading backend, or NULL if no
|
||||
* callbacks are registered.
|
||||
*/
|
||||
const char *ssh_threads_get_type(void)
|
||||
{
|
||||
if (user_callbacks != NULL) {
|
||||
|
||||
@@ -437,6 +437,7 @@ int crypt_set_algorithms_server(ssh_session session){
|
||||
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
|
||||
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
|
||||
int cmp;
|
||||
int rc;
|
||||
|
||||
if (session == NULL) {
|
||||
return SSH_ERROR;
|
||||
@@ -580,8 +581,24 @@ int crypt_set_algorithms_server(ssh_session session){
|
||||
}
|
||||
|
||||
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];
|
||||
session->srv.hostkey = ssh_key_type_from_signature_name(method);
|
||||
session->srv.hostkey_digest = ssh_key_hash_from_name(method);
|
||||
|
||||
/* For GSSAPI key exchange, hostkey algorithm may be "null" */
|
||||
if (strcmp(method, "null") == 0) {
|
||||
session->srv.hostkey = SSH_KEYTYPE_UNKNOWN;
|
||||
session->srv.hostkey_digest = SSH_DIGEST_AUTO;
|
||||
} else {
|
||||
rc = ssh_key_type_and_hash_from_signature_name(
|
||||
method,
|
||||
&session->srv.hostkey,
|
||||
&session->srv.hostkey_digest);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"unknown hostkey algorithm %s",
|
||||
method);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* setup DH key exchange type */
|
||||
switch (session->next_crypto->kex_type) {
|
||||
|
||||
@@ -259,6 +259,20 @@ else()
|
||||
set(PUTTYGEN_EXECUTABLE "/bin/puttygen-not-found")
|
||||
endif()
|
||||
|
||||
find_program(TINYSSHD_EXECUTABLE
|
||||
NAMES
|
||||
tinysshd
|
||||
PATHS
|
||||
/sbin
|
||||
/usr/sbin
|
||||
/usr/local/sbin)
|
||||
|
||||
if (TINYSSHD_EXECUTABLE)
|
||||
message(STATUS "Found TinySSH server: ${TINYSSHD_EXECUTABLE}")
|
||||
else()
|
||||
message(STATUS "Could NOT find TinySSH server")
|
||||
endif()
|
||||
|
||||
find_program(SSHD_EXECUTABLE
|
||||
NAME
|
||||
sshd
|
||||
@@ -391,6 +405,14 @@ if (CLIENT_TESTING OR SERVER_TESTING)
|
||||
file(COPY keys/id_ed25519_sk DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
|
||||
file(COPY keys/id_ed25519_sk.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
|
||||
|
||||
# TINYSSH Key Setup
|
||||
set(TINYSSH_KEY_DIR "${CMAKE_CURRENT_BINARY_DIR}/etc/tinyssh")
|
||||
file(MAKE_DIRECTORY ${TINYSSH_KEY_DIR})
|
||||
|
||||
# Copy the TINYSSH hostkeys
|
||||
file(COPY keys/ed25519.pk DESTINATION ${TINYSSH_KEY_DIR})
|
||||
file(COPY keys/.ed25519.sk DESTINATION ${TINYSSH_KEY_DIR})
|
||||
|
||||
# Allow to auth with bob's public keys on alice and doe account
|
||||
configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/alice/.ssh/authorized_keys @ONLY)
|
||||
configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/authorized_keys @ONLY)
|
||||
@@ -419,6 +441,9 @@ if (CLIENT_TESTING OR SERVER_TESTING)
|
||||
file(READ keys/pkcs11/id_pkcs11_ecdsa_256_openssh.pub CONTENTS)
|
||||
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
|
||||
|
||||
# Create home directory for noneuser (for "none" authentication test)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/home/noneuser/.ssh)
|
||||
|
||||
file(READ keys/pkcs11/id_pkcs11_ecdsa_384_openssh.pub CONTENTS)
|
||||
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ if (WITH_PKCS11_URI)
|
||||
torture_auth_pkcs11)
|
||||
endif()
|
||||
|
||||
if (TINYSSHD_EXECUTABLE AND NCAT_EXECUTABLE)
|
||||
set(LIBSSH_CLIENT_TESTS ${LIBSSH_CLIENT_TESTS} torture_tinyssh)
|
||||
endif()
|
||||
|
||||
if (HAVE_PTHREAD)
|
||||
set(LIBSSH_CLIENT_TESTS
|
||||
${LIBSSH_CLIENT_TESTS}
|
||||
@@ -92,6 +96,12 @@ foreach(_CLI_TEST ${LIBSSH_CLIENT_TESTS})
|
||||
LINK_LIBRARIES ${TORTURE_LIBRARY} util
|
||||
)
|
||||
|
||||
if (_CLI_TEST STREQUAL "torture_tinyssh")
|
||||
target_compile_definitions(${_CLI_TEST} PRIVATE
|
||||
TINYSSH_KEYS_DIR="${TINYSSH_KEY_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (OSX)
|
||||
set_property(
|
||||
TEST
|
||||
|
||||
@@ -228,6 +228,44 @@ static void torture_auth_none_max_tries(void **state) {
|
||||
torture_update_sshd_config(state, "");
|
||||
}
|
||||
|
||||
static void torture_auth_none_success(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
const char *additional_config = "PermitEmptyPasswords yes\n"
|
||||
"PasswordAuthentication yes\n"
|
||||
"KbdInteractiveAuthentication no\n"
|
||||
"PubkeyAuthentication no\n"
|
||||
"AuthenticationMethods none\n";
|
||||
|
||||
ssh_session session = s->ssh.session;
|
||||
int rc;
|
||||
|
||||
torture_update_sshd_config(state, additional_config);
|
||||
|
||||
/* Use noneuser which has an empty password set in shadow.in
|
||||
* When PermitEmptyPasswords is yes and PasswordAuthentication is yes,
|
||||
* OpenSSH's userauth_none() internally calls mm_auth_password() with
|
||||
* an empty password, which succeeds for users with empty passwords.
|
||||
*/
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_NONEUSER);
|
||||
if (rc != SSH_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_connect(session);
|
||||
if (rc != SSH_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = ssh_userauth_none(session, NULL);
|
||||
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||
|
||||
cleanup:
|
||||
torture_update_sshd_config(state, "");
|
||||
if (rc != SSH_OK && rc != SSH_AUTH_SUCCESS) {
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
}
|
||||
}
|
||||
|
||||
static void torture_auth_pubkey(void **state) {
|
||||
struct torture_state *s = *state;
|
||||
@@ -376,7 +414,7 @@ torture_auth_autopubkey_protected_auth_function (const char *prompt, char *buf,
|
||||
assert_int_equal(echo, 0);
|
||||
assert_int_equal(verify, 0);
|
||||
|
||||
expected_id = ssh_path_expand_escape(data->session, "%d/id_rsa_protected");
|
||||
expected_id = ssh_path_expand_escape(data->session, "%d/.ssh/id_rsa_protected");
|
||||
assert_true(expected_id != NULL);
|
||||
|
||||
rc = ssh_userauth_publickey_auto_get_current_identity(data->session, &id);
|
||||
@@ -429,7 +467,7 @@ static void torture_auth_autopubkey_protected(void **state) {
|
||||
|
||||
/* Try id_rsa_protected first.
|
||||
*/
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/id_rsa_protected");
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/.ssh/id_rsa_protected");
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
@@ -1373,6 +1411,9 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_none_nonblocking,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_none_success,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_none_max_tries,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
@@ -1424,9 +1465,10 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only,
|
||||
agent_setup,
|
||||
agent_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only_protected,
|
||||
agent_setup,
|
||||
agent_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_auth_agent_identities_only_protected,
|
||||
agent_setup,
|
||||
agent_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
@@ -1436,15 +1478,17 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ecdsa,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ecdsa_nonblocking,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_auth_pubkey_types_ecdsa_nonblocking,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ed25519,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ed25519_nonblocking,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_auth_pubkey_types_ed25519_nonblocking,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
#ifdef WITH_FIDO2
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_sk_ecdsa,
|
||||
pubkey_setup,
|
||||
@@ -1456,9 +1500,10 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size_nonblocking,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_auth_pubkey_rsa_key_size_nonblocking,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_auth_pubkey_skip_none,
|
||||
pubkey_setup,
|
||||
session_teardown),
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "libssh/callbacks.h"
|
||||
#include "libssh/libssh.h"
|
||||
#include <libssh/agent.h>
|
||||
#include "libssh/priv.h"
|
||||
|
||||
#include <errno.h>
|
||||
@@ -286,6 +287,96 @@ static void torture_auth_agent_forwarding(void **state)
|
||||
}
|
||||
}
|
||||
|
||||
static int agent_session_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
int rc;
|
||||
|
||||
s->ssh.ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.ssh.session);
|
||||
|
||||
rc = ssh_options_set(s->ssh.ssh.session,
|
||||
SSH_OPTIONS_LOG_VERBOSITY,
|
||||
&verbosity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
/* No callbacks needed — only talking to the local agent.
|
||||
* The group setup already started the agent and loaded keys.
|
||||
* Do NOT call torture_setup_ssh_agent here — that would spawn
|
||||
* a second agent and overwrite SSH_AUTH_SOCK. */
|
||||
s->ssh.ssh.cb_state = NULL;
|
||||
s->ssh.ssh.callbacks = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void torture_agent_remove_identity(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.ssh.session;
|
||||
ssh_key key = NULL;
|
||||
char *comment = NULL;
|
||||
uint32_t count_before = 0;
|
||||
uint32_t count_after = 0;
|
||||
int rc;
|
||||
|
||||
assert_non_null(session);
|
||||
|
||||
assert_true(ssh_agent_is_running(session));
|
||||
|
||||
count_before = ssh_agent_get_ident_count(session);
|
||||
|
||||
assert_true(count_before > 0);
|
||||
|
||||
key = ssh_agent_get_first_ident(session, &comment);
|
||||
assert_non_null(key);
|
||||
assert_non_null(comment);
|
||||
|
||||
rc = ssh_agent_remove_identity(session, key);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
count_after = ssh_agent_get_ident_count(session);
|
||||
assert_int_equal(count_after, count_before - 1);
|
||||
|
||||
ssh_key_free(key);
|
||||
ssh_string_free_char(comment);
|
||||
}
|
||||
|
||||
static void torture_agent_remove_identity_negative(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.ssh.session;
|
||||
int rc;
|
||||
|
||||
assert_non_null(session);
|
||||
|
||||
/* NULL key should return SSH_ERROR */
|
||||
rc = ssh_agent_remove_identity(session, NULL);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
}
|
||||
|
||||
static void torture_agent_remove_identity_nonexistent(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
ssh_session session = s->ssh.ssh.session;
|
||||
ssh_key key = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(session);
|
||||
assert_true(ssh_agent_is_running(session));
|
||||
|
||||
rc = ssh_pki_generate_key(SSH_KEYTYPE_RSA, NULL, &key);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_non_null(key);
|
||||
|
||||
/* Key not in agent should fail */
|
||||
rc = ssh_agent_remove_identity(session, key);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
ssh_key_free(key);
|
||||
}
|
||||
|
||||
/* Session setup function that configures SSH agent */
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
@@ -300,7 +391,6 @@ static int session_setup(void **state)
|
||||
/* Create a new session */
|
||||
s->ssh.ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.ssh.session);
|
||||
|
||||
rc = ssh_options_set(s->ssh.ssh.session,
|
||||
SSH_OPTIONS_LOG_VERBOSITY,
|
||||
&verbosity);
|
||||
@@ -342,19 +432,32 @@ static int session_setup(void **state)
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_auth_agent_forwarding,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_agent_remove_identity,
|
||||
agent_session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_agent_remove_identity_negative,
|
||||
agent_session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_agent_remove_identity_nonexistent,
|
||||
agent_session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
/* Simplify the CMocka test filter handling */
|
||||
#if defined HAVE_CMOCKA_SET_TEST_FILTER
|
||||
cmocka_set_message_output(CM_OUTPUT_STDOUT);
|
||||
#endif
|
||||
|
||||
/* Apply test filtering */
|
||||
torture_filter_tests(tests);
|
||||
|
||||
rc = cmocka_run_group_tests(tests,
|
||||
@@ -365,5 +468,4 @@ int torture_run_tests(void)
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -100,13 +100,10 @@ static int session_setup(void **state)
|
||||
static int session_setup_ssh_dir(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
const char *no_home = "~/.no_ssh";
|
||||
int rc;
|
||||
|
||||
session_setup(state);
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_SSH_DIR, no_home);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
s->ssh.session->opts.homedir = strdup("~/.no_ssh");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <errno.h>
|
||||
#include "torture.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/misc.h"
|
||||
|
||||
#define LIBSSH_SSH_CONFIG "libssh_config"
|
||||
@@ -59,6 +60,23 @@ static int setup_config_files(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_session(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity;
|
||||
|
||||
s->ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
verbosity = torture_libssh_verbosity();
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
|
||||
setenv("NSS_WRAPPER_HOSTNAME", "client.libssh.site", 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -80,6 +98,16 @@ static int teardown(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int teardown_session(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
ssh_disconnect(s->ssh.session);
|
||||
ssh_free(s->ssh.session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This tests makes sure that parsing both system-wide and per-user
|
||||
* configuration files retains OpenSSH semantics (the per-user overrides
|
||||
* the system-wide values).
|
||||
@@ -210,10 +238,233 @@ static void torture_client_config_suppress(void **state)
|
||||
assert_string_equal(s->ssh.session->opts.username, "bob");
|
||||
}
|
||||
|
||||
static void torture_client_config_expand_bad(void **state)
|
||||
{
|
||||
ssh_session session = ssh_new();
|
||||
int ret = 0;
|
||||
|
||||
(void)state;
|
||||
|
||||
assert_non_null(session);
|
||||
|
||||
/* The hash without host fails, but does not crash */
|
||||
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "%C");
|
||||
|
||||
ret = ssh_options_apply(session);
|
||||
assert_ssh_return_code_equal(session, ret, SSH_ERROR);
|
||||
|
||||
/* The hash without host fails, but does not crash */
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "%C");
|
||||
|
||||
ret = ssh_options_apply(session);
|
||||
assert_ssh_return_code_equal(session, ret, SSH_OK);
|
||||
|
||||
ssh_free(session);
|
||||
}
|
||||
|
||||
static void torture_client_config_expand(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int ret = 0;
|
||||
|
||||
/* TEST: user home directory */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%d");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
BINARYDIR "/tests/home");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: target host name */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%h");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, TORTURE_SSH_SERVER);
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: local username */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%u");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "root");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: local hostname */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "client.libssh.site");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: remote username */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, "alice");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%r");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "alice");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: remote port */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT_STR, "2222");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%p");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "2222");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: empty proxyjump */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%j");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
/* No proxyjump string should not explode */
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: proxyjump string present */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%j");
|
||||
ssh_options_set(s->ssh.session,
|
||||
SSH_OPTIONS_PROXYJUMP,
|
||||
"user@" TORTURE_SSH_SERVER ":22");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"user@" TORTURE_SSH_SERVER ":22");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: separate list %l-%h-%p-%r-%j with empty ProxyJump */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l-%h-%p-%r-%j");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_PROXYJUMP, "none");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT_STR, "22");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%l-%h-%p-%r-%j\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"client.libssh.site-127.0.0.10-22-alice-");
|
||||
|
||||
|
||||
/* TEST: hash of %l%h%p%r%j with empty ProxyJump */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%C");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%C\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"133e3957ff9d01fdcf1f6c7f83325a8ce49bf850");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: separate list %l-%h-%p-%r-%j */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l-%h-%p-%r-%j");
|
||||
ssh_options_set(s->ssh.session,
|
||||
SSH_OPTIONS_PROXYJUMP,
|
||||
"user@" TORTURE_SSH_SERVER ":22");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -oProxyJump=user@" TORTURE_SSH_SERVER ":22"
|
||||
// " -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%l-%h-%p-%r-%j\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"client.libssh.site-127.0.0.10-22-alice-user@"
|
||||
TORTURE_SSH_SERVER ":22");
|
||||
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: hash of %l%h%p%r%j */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%C");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -oProxyJump=user@" TORTURE_SSH_SERVER ":22"
|
||||
// " -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%C\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"adf0b7c4e71a0fee85fd97506507ba8591f3663b");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int torture_run_tests(void) {
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
/* Keep these first -- following setup is changing user to bob, which we
|
||||
* do not want */
|
||||
cmocka_unit_test(torture_client_config_expand_bad),
|
||||
cmocka_unit_test_setup_teardown(torture_client_config_expand,
|
||||
setup_session,
|
||||
teardown_session),
|
||||
cmocka_unit_test_setup_teardown(torture_client_config_system,
|
||||
setup_config_files,
|
||||
teardown),
|
||||
@@ -228,7 +479,6 @@ int torture_run_tests(void) {
|
||||
teardown),
|
||||
};
|
||||
|
||||
|
||||
ssh_init();
|
||||
torture_filter_tests(tests);
|
||||
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
|
||||
|
||||
@@ -167,7 +167,7 @@ static void torture_connect_addrfamily(void **state)
|
||||
{SSH_ADDRESS_FAMILY_INET6, "afinet6", SSH_OK},
|
||||
};
|
||||
|
||||
int aftest_count = sizeof(aftests) / sizeof(aftests[0]);
|
||||
int aftest_count = ARRAY_SIZE(aftests);
|
||||
for (int i = 0; i < aftest_count; ++i) {
|
||||
struct aftest const *t = &aftests[i];
|
||||
|
||||
|
||||
@@ -94,8 +94,7 @@ static void torture_kex_basic_functionality(void **state)
|
||||
assert_non_null(kex_algo);
|
||||
|
||||
is_valid_algo = false;
|
||||
valid_algorithms_count =
|
||||
sizeof(valid_algorithms) / sizeof(valid_algorithms[0]);
|
||||
valid_algorithms_count = ARRAY_SIZE(valid_algorithms);
|
||||
for (i = 0; i < valid_algorithms_count; i++) {
|
||||
if (strcmp(kex_algo, valid_algorithms[i]) == 0) {
|
||||
is_valid_algo = true;
|
||||
|
||||
@@ -11,19 +11,37 @@
|
||||
|
||||
#define MAX_XFER_BUF_SIZE 16384
|
||||
|
||||
#define DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(TEST_NAME) \
|
||||
{ \
|
||||
#TEST_NAME, \
|
||||
TEST_NAME, \
|
||||
session_setup, \
|
||||
session_teardown, \
|
||||
NULL \
|
||||
}, \
|
||||
{ \
|
||||
#TEST_NAME"_proxyjump", \
|
||||
TEST_NAME, \
|
||||
session_proxyjump_setup, \
|
||||
session_teardown, \
|
||||
NULL \
|
||||
}
|
||||
|
||||
static int sshd_setup(void **state)
|
||||
{
|
||||
torture_setup_sshd_server(state, false);
|
||||
torture_setup_sshd_servers(state, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshd_teardown(void **state)
|
||||
{
|
||||
/* this will take care of the server1 teardown too */
|
||||
torture_teardown_sshd_server(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
static int session_setup_helper(void **state, bool with_proxyjump)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
struct passwd *pwd = NULL;
|
||||
@@ -35,11 +53,15 @@ static int session_setup(void **state)
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = torture_ssh_session(s,
|
||||
TORTURE_SSH_SERVER,
|
||||
NULL,
|
||||
TORTURE_SSH_USER_ALICE,
|
||||
NULL);
|
||||
if (with_proxyjump) {
|
||||
s->ssh.session = torture_ssh_session_proxyjump();
|
||||
} else {
|
||||
s->ssh.session = torture_ssh_session(s,
|
||||
TORTURE_SSH_SERVER,
|
||||
NULL,
|
||||
TORTURE_SSH_USER_ALICE,
|
||||
NULL);
|
||||
}
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
s->ssh.tsftp = torture_sftp_session(s->ssh.session);
|
||||
@@ -48,6 +70,16 @@ static int session_setup(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
return session_setup_helper(state, false);
|
||||
}
|
||||
|
||||
static int session_proxyjump_setup(void **state)
|
||||
{
|
||||
return session_setup_helper(state, true);
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -722,37 +754,18 @@ int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_file,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_more_than_cap,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_file,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_more_than_cap,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_negative,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_negative,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_unordered_wait,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_unordered_wait,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_file),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_read_more_than_cap),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_file),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_write_more_than_cap),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_negative),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_negative),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_read_unordered_wait),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_write_unordered_wait),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
@@ -72,6 +72,63 @@ static void session_setup_channel(void **state)
|
||||
assert_non_null(s->ssh.tsftp);
|
||||
}
|
||||
|
||||
static void session_setup_extensions(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
struct passwd *pwd = NULL;
|
||||
int rc, count;
|
||||
const char *name = NULL, *data = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
|
||||
pwd = getpwnam("bob");
|
||||
assert_non_null(pwd);
|
||||
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = torture_ssh_session(s,
|
||||
TORTURE_SSH_SERVER,
|
||||
NULL,
|
||||
TORTURE_SSH_USER_ALICE,
|
||||
NULL);
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
s->ssh.tsftp = torture_sftp_session(s->ssh.session);
|
||||
assert_non_null(s->ssh.tsftp);
|
||||
sftp = s->ssh.tsftp->sftp;
|
||||
|
||||
/* null parameter */
|
||||
count = sftp_extensions_get_count(NULL);
|
||||
assert_int_equal(count, 0);
|
||||
|
||||
count = sftp_extensions_get_count(sftp);
|
||||
assert_int_not_equal(count, 0);
|
||||
|
||||
/* first null parameter */
|
||||
name = sftp_extensions_get_name(NULL, 0);
|
||||
assert_null(name);
|
||||
data = sftp_extensions_get_data(NULL, 0);
|
||||
assert_null(data);
|
||||
|
||||
/* First extension */
|
||||
name = sftp_extensions_get_name(sftp, 0);
|
||||
assert_non_null(name);
|
||||
data = sftp_extensions_get_data(sftp, 0);
|
||||
assert_non_null(data);
|
||||
|
||||
/* Last extension */
|
||||
name = sftp_extensions_get_name(sftp, count - 1);
|
||||
assert_non_null(name);
|
||||
data = sftp_extensions_get_data(sftp, count - 1);
|
||||
assert_non_null(data);
|
||||
|
||||
/* Overrun */
|
||||
name = sftp_extensions_get_name(sftp, count);
|
||||
assert_null(name);
|
||||
data = sftp_extensions_get_data(sftp, count);
|
||||
assert_null(data);
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -92,7 +149,10 @@ int torture_run_tests(void) {
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(session_setup_channel,
|
||||
NULL,
|
||||
session_teardown)
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(session_setup_extensions,
|
||||
NULL,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
328
tests/client/torture_tinyssh.c
Normal file
328
tests/client/torture_tinyssh.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* Copyright (c) 2026 by Your Bulitha Kawushika De Zoysa
|
||||
*
|
||||
* 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 "tests_config.h"
|
||||
|
||||
#define LIBSSH_STATIC
|
||||
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/session.h"
|
||||
#include "torture.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TINYSSH_PIDFILE "tinyssh.pid"
|
||||
#define TINYSSH_PORT 22
|
||||
|
||||
/* TINYSSH Server Setup and Teardown */
|
||||
|
||||
static int tinyssh_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = NULL;
|
||||
char cmd[4096];
|
||||
char pid_path[1024];
|
||||
int rc;
|
||||
|
||||
torture_setup_socket_dir(state);
|
||||
s = *state;
|
||||
|
||||
snprintf(pid_path,
|
||||
sizeof(pid_path),
|
||||
"%s/%s",
|
||||
s->socket_dir,
|
||||
TINYSSH_PIDFILE);
|
||||
free(s->srv_pidfile);
|
||||
s->srv_pidfile = strdup(pid_path);
|
||||
if (s->srv_pidfile == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(cmd,
|
||||
sizeof(cmd),
|
||||
"%s -l %s %d -k -c \"%s %s -v %s\" "
|
||||
"> %s/tinyssh.log 2>&1 & echo $! > %s",
|
||||
NCAT_EXECUTABLE,
|
||||
TORTURE_SSH_SERVER,
|
||||
TINYSSH_PORT,
|
||||
TINYSSHD_EXECUTABLE,
|
||||
"",
|
||||
TINYSSH_KEYS_DIR,
|
||||
s->socket_dir,
|
||||
s->srv_pidfile);
|
||||
|
||||
SSH_LOG(SSH_LOG_DEBUG, "Executing: %s\n", cmd);
|
||||
|
||||
rc = system(cmd);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = torture_wait_for_daemon(15);
|
||||
if (rc != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tinyssh_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
torture_terminate_process(s->srv_pidfile);
|
||||
torture_teardown_socket_dir(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LIBSSH Client Setup and Teardown */
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
bool process_config = false;
|
||||
int port = TINYSSH_PORT;
|
||||
struct passwd *pwd = NULL;
|
||||
int rc;
|
||||
|
||||
pwd = getpwnam("bob");
|
||||
assert_non_null(pwd);
|
||||
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = 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_PORT, &port);
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, "bob");
|
||||
ssh_options_set(s->ssh.session,
|
||||
SSH_OPTIONS_PROCESS_CONFIG,
|
||||
&process_config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
if (s->ssh.session) {
|
||||
ssh_disconnect(s->ssh.session);
|
||||
ssh_free(s->ssh.session);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Algorithms Helper */
|
||||
|
||||
static void test_specific_algorithm(ssh_session session,
|
||||
const char *kex,
|
||||
const char *cipher,
|
||||
const char *hostkey,
|
||||
int expected_rc)
|
||||
{
|
||||
int rc;
|
||||
char data[256];
|
||||
size_t len_to_test[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15,
|
||||
16, 20, 31, 32, 33, 63, 64, 65, 100, 127, 128};
|
||||
unsigned int i;
|
||||
|
||||
/* Set Key Exchange */
|
||||
if (kex != NULL) {
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex);
|
||||
assert_ssh_return_code(session, rc);
|
||||
}
|
||||
|
||||
/* Set Ciphers */
|
||||
if (cipher != NULL) {
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher);
|
||||
assert_ssh_return_code(session, rc);
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher);
|
||||
assert_ssh_return_code(session, rc);
|
||||
}
|
||||
|
||||
/* Set Hostkey */
|
||||
if (hostkey != NULL) {
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, hostkey);
|
||||
assert_ssh_return_code(session, rc);
|
||||
}
|
||||
|
||||
rc = ssh_connect(session);
|
||||
|
||||
if (expected_rc == SSH_OK) {
|
||||
assert_ssh_return_code(session, rc);
|
||||
|
||||
if (cipher != NULL) {
|
||||
const char *used_cipher = ssh_get_cipher_out(session);
|
||||
assert_non_null(used_cipher);
|
||||
assert_string_equal(used_cipher, cipher);
|
||||
}
|
||||
|
||||
if (hostkey != NULL) {
|
||||
ssh_key pubkey = NULL;
|
||||
const char *type_str = NULL;
|
||||
|
||||
rc = ssh_get_server_publickey(session, &pubkey);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_non_null(pubkey);
|
||||
|
||||
type_str = ssh_key_type_to_char(ssh_key_type(pubkey));
|
||||
assert_non_null(type_str);
|
||||
assert_string_equal(type_str, hostkey);
|
||||
ssh_key_free(pubkey);
|
||||
}
|
||||
|
||||
memset(data, 0, sizeof(data));
|
||||
for (i = 0; i < (sizeof(len_to_test) / sizeof(size_t)); i++) {
|
||||
memset(data, 'A', len_to_test[i]);
|
||||
ssh_send_ignore(session, data);
|
||||
ssh_handle_packets(session, 50);
|
||||
}
|
||||
|
||||
rc = ssh_userauth_none(session, NULL);
|
||||
if (rc != SSH_OK) {
|
||||
rc = ssh_get_error_code(session);
|
||||
assert_int_equal(rc, SSH_REQUEST_DENIED);
|
||||
}
|
||||
|
||||
} else {
|
||||
assert_int_not_equal(rc, SSH_OK);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test Cases */
|
||||
|
||||
static void torture_tinyssh_curve25519(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
test_specific_algorithm(s->ssh.session,
|
||||
"curve25519-sha256",
|
||||
NULL,
|
||||
NULL,
|
||||
SSH_OK);
|
||||
}
|
||||
|
||||
static void torture_tinyssh_curve25519_libssh(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
test_specific_algorithm(s->ssh.session,
|
||||
"curve25519-sha256@libssh.org",
|
||||
NULL,
|
||||
NULL,
|
||||
SSH_OK);
|
||||
}
|
||||
|
||||
static void torture_tinyssh_sntrup761(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
test_specific_algorithm(s->ssh.session,
|
||||
"sntrup761x25519-sha512@openssh.com",
|
||||
NULL,
|
||||
NULL,
|
||||
SSH_OK);
|
||||
}
|
||||
|
||||
static void torture_tinyssh_chacha20(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
test_specific_algorithm(s->ssh.session,
|
||||
NULL,
|
||||
"chacha20-poly1305@openssh.com",
|
||||
NULL,
|
||||
SSH_OK);
|
||||
}
|
||||
|
||||
static void torture_tinyssh_neg_cipher(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
/* TinySSH does not support older ciphers like aes128-cbc.*/
|
||||
|
||||
test_specific_algorithm(s->ssh.session,
|
||||
NULL,
|
||||
"aes128-cbc",
|
||||
NULL,
|
||||
SSH_ERROR);
|
||||
}
|
||||
|
||||
static void torture_tinyssh_hostkey_ed25519(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
test_specific_algorithm(s->ssh.session, NULL, NULL, "ssh-ed25519", SSH_OK);
|
||||
}
|
||||
|
||||
static void torture_tinyssh_neg_kex(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
/* TinySSH does not support legacy Diffie-Hellman groups or NIST curves.*/
|
||||
|
||||
test_specific_algorithm(s->ssh.session,
|
||||
"diffie-hellman-group1-sha1",
|
||||
NULL,
|
||||
NULL,
|
||||
SSH_ERROR);
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_curve25519,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_curve25519_libssh,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_sntrup761,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_hostkey_ed25519,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_chacha20,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_neg_cipher,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_tinyssh_neg_kex,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
torture_filter_tests(tests);
|
||||
rc = cmocka_run_group_tests(tests, tinyssh_setup, tinyssh_teardown);
|
||||
|
||||
ssh_finalize();
|
||||
return rc;
|
||||
}
|
||||
@@ -2,4 +2,4 @@ users:x:9000:
|
||||
sshd:x:65531:
|
||||
nobody:x:65533:
|
||||
nogroup:x:65534:nobody
|
||||
root:x:65532:
|
||||
root:x:0:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user