aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ABI/current2
-rw-r--r--src/ABI/libssh-4.9.0.symbols427
-rw-r--r--src/CMakeLists.txt125
-rw-r--r--src/agent.c100
-rw-r--r--src/auth.c676
-rw-r--r--src/bignum.c15
-rw-r--r--src/bind.c296
-rw-r--r--src/bind_config.c149
-rw-r--r--src/buffer.c49
-rw-r--r--src/chachapoly.c4
-rw-r--r--src/channels.c959
-rw-r--r--src/client.c476
-rw-r--r--src/config.c351
-rw-r--r--src/config_parser.c45
-rw-r--r--src/connect.c54
-rw-r--r--src/connector.c70
-rw-r--r--src/crypto_common.c36
-rw-r--r--src/curve25519.c62
-rw-r--r--src/dh-gex.c70
-rw-r--r--src/dh.c60
-rw-r--r--src/dh_crypto.c341
-rw-r--r--src/dh_key.c4
-rw-r--r--src/ecdh.c15
-rw-r--r--src/ecdh_crypto.c464
-rw-r--r--src/ecdh_gcrypt.c16
-rw-r--r--src/ecdh_mbedcrypto.c67
-rw-r--r--src/error.c8
-rw-r--r--src/external/bcrypt_pbkdf.c32
-rw-r--r--src/external/ed25519.c8
-rw-r--r--src/gcrypt_missing.c2
-rw-r--r--src/getpass.c21
-rw-r--r--src/getrandom_crypto.c64
-rw-r--r--src/getrandom_gcrypt.c38
-rw-r--r--src/getrandom_mbedcrypto.c52
-rw-r--r--src/gssapi.c166
-rw-r--r--src/gzip.c342
-rw-r--r--src/init.c32
-rw-r--r--src/kdf.c129
-rw-r--r--src/kex.c852
-rw-r--r--src/known_hosts.c49
-rw-r--r--src/knownhosts.c119
-rw-r--r--src/legacy.c76
-rw-r--r--src/libcrypto-compat.c411
-rw-r--r--src/libcrypto-compat.h51
-rw-r--r--src/libcrypto.c1010
-rw-r--r--src/libgcrypt.c252
-rw-r--r--src/libmbedcrypto.c500
-rw-r--r--src/libssh.map29
-rw-r--r--src/log.c50
-rw-r--r--src/match.c123
-rw-r--r--src/mbedcrypto-compat.h39
-rw-r--r--src/mbedcrypto_missing.c6
-rw-r--r--src/md_crypto.c324
-rw-r--r--src/md_gcrypt.c246
-rw-r--r--src/md_mbedcrypto.c407
-rw-r--r--src/messages.c169
-rw-r--r--src/misc.c699
-rw-r--r--src/options.c1336
-rw-r--r--src/packet.c267
-rw-r--r--src/packet_cb.c268
-rw-r--r--src/packet_crypt.c147
-rw-r--r--src/pcap.c277
-rw-r--r--src/pki.c835
-rw-r--r--src/pki_container_openssh.c126
-rw-r--r--src/pki_crypto.c2162
-rw-r--r--src/pki_ed25519.c2
-rw-r--r--src/pki_ed25519_common.c84
-rw-r--r--src/pki_gcrypt.c664
-rw-r--r--src/pki_mbedcrypto.c950
-rw-r--r--src/poll.c302
-rw-r--r--src/scp.c107
-rw-r--r--src/server.c395
-rw-r--r--src/session.c264
-rw-r--r--src/sftp.c1948
-rw-r--r--src/sftp_aio.c500
-rw-r--r--src/sftp_common.c913
-rw-r--r--src/sftpserver.c2028
-rw-r--r--src/socket.c183
-rw-r--r--src/string.c8
-rw-r--r--src/threads.c2
-rw-r--r--src/threads/libcrypto.c97
-rw-r--r--src/threads/winlocks.c1
-rw-r--r--src/token.c143
-rw-r--r--src/ttyopts.c449
-rw-r--r--src/wrapper.c121
85 files changed, 16800 insertions, 9018 deletions
diff --git a/src/ABI/current b/src/ABI/current
index 28715673..b617d997 100644
--- a/src/ABI/current
+++ b/src/ABI/current
@@ -1 +1 @@
-4.8.1 \ No newline at end of file
+4.9.0 \ No newline at end of file
diff --git a/src/ABI/libssh-4.9.0.symbols b/src/ABI/libssh-4.9.0.symbols
new file mode 100644
index 00000000..a26e2c5e
--- /dev/null
+++ b/src/ABI/libssh-4.9.0.symbols
@@ -0,0 +1,427 @@
+_ssh_log
+buffer_free
+buffer_get
+buffer_get_len
+buffer_new
+channel_accept_x11
+channel_change_pty_size
+channel_close
+channel_forward_accept
+channel_forward_cancel
+channel_forward_listen
+channel_free
+channel_get_exit_status
+channel_get_session
+channel_is_closed
+channel_is_eof
+channel_is_open
+channel_new
+channel_open_forward
+channel_open_session
+channel_poll
+channel_read
+channel_read_buffer
+channel_read_nonblocking
+channel_request_env
+channel_request_exec
+channel_request_pty
+channel_request_pty_size
+channel_request_send_signal
+channel_request_sftp
+channel_request_shell
+channel_request_subsystem
+channel_request_x11
+channel_select
+channel_send_eof
+channel_set_blocking
+channel_write
+channel_write_stderr
+privatekey_free
+privatekey_from_file
+publickey_free
+publickey_from_file
+publickey_from_privatekey
+publickey_to_string
+sftp_async_read
+sftp_async_read_begin
+sftp_attributes_free
+sftp_canonicalize_path
+sftp_chmod
+sftp_chown
+sftp_client_message_free
+sftp_client_message_get_data
+sftp_client_message_get_filename
+sftp_client_message_get_flags
+sftp_client_message_get_submessage
+sftp_client_message_get_type
+sftp_client_message_set_filename
+sftp_close
+sftp_closedir
+sftp_dir_eof
+sftp_extension_supported
+sftp_extensions_get_count
+sftp_extensions_get_data
+sftp_extensions_get_name
+sftp_file_set_blocking
+sftp_file_set_nonblocking
+sftp_free
+sftp_fstat
+sftp_fstatvfs
+sftp_fsync
+sftp_get_client_message
+sftp_get_error
+sftp_handle
+sftp_handle_alloc
+sftp_handle_remove
+sftp_init
+sftp_lstat
+sftp_mkdir
+sftp_new
+sftp_new_channel
+sftp_open
+sftp_opendir
+sftp_read
+sftp_readdir
+sftp_readlink
+sftp_rename
+sftp_reply_attr
+sftp_reply_data
+sftp_reply_handle
+sftp_reply_name
+sftp_reply_names
+sftp_reply_names_add
+sftp_reply_status
+sftp_rewind
+sftp_rmdir
+sftp_seek
+sftp_seek64
+sftp_send_client_message
+sftp_server_free
+sftp_server_init
+sftp_server_new
+sftp_server_version
+sftp_setstat
+sftp_stat
+sftp_statvfs
+sftp_statvfs_free
+sftp_symlink
+sftp_tell
+sftp_tell64
+sftp_unlink
+sftp_utimes
+sftp_write
+ssh_accept
+ssh_add_channel_callbacks
+ssh_auth_list
+ssh_basename
+ssh_bind_accept
+ssh_bind_accept_fd
+ssh_bind_fd_toaccept
+ssh_bind_free
+ssh_bind_get_fd
+ssh_bind_listen
+ssh_bind_new
+ssh_bind_options_parse_config
+ssh_bind_options_set
+ssh_bind_set_blocking
+ssh_bind_set_callbacks
+ssh_bind_set_fd
+ssh_blocking_flush
+ssh_buffer_add_data
+ssh_buffer_free
+ssh_buffer_get
+ssh_buffer_get_data
+ssh_buffer_get_len
+ssh_buffer_new
+ssh_buffer_reinit
+ssh_channel_accept_forward
+ssh_channel_accept_x11
+ssh_channel_cancel_forward
+ssh_channel_change_pty_size
+ssh_channel_close
+ssh_channel_free
+ssh_channel_get_exit_status
+ssh_channel_get_session
+ssh_channel_is_closed
+ssh_channel_is_eof
+ssh_channel_is_open
+ssh_channel_listen_forward
+ssh_channel_new
+ssh_channel_open_auth_agent
+ssh_channel_open_forward
+ssh_channel_open_forward_port
+ssh_channel_open_forward_unix
+ssh_channel_open_reverse_forward
+ssh_channel_open_session
+ssh_channel_open_x11
+ssh_channel_poll
+ssh_channel_poll_timeout
+ssh_channel_read
+ssh_channel_read_nonblocking
+ssh_channel_read_timeout
+ssh_channel_request_auth_agent
+ssh_channel_request_env
+ssh_channel_request_exec
+ssh_channel_request_pty
+ssh_channel_request_pty_size
+ssh_channel_request_send_break
+ssh_channel_request_send_exit_signal
+ssh_channel_request_send_exit_status
+ssh_channel_request_send_signal
+ssh_channel_request_sftp
+ssh_channel_request_shell
+ssh_channel_request_subsystem
+ssh_channel_request_x11
+ssh_channel_select
+ssh_channel_send_eof
+ssh_channel_set_blocking
+ssh_channel_set_counter
+ssh_channel_window_size
+ssh_channel_write
+ssh_channel_write_stderr
+ssh_clean_pubkey_hash
+ssh_connect
+ssh_connector_free
+ssh_connector_new
+ssh_connector_set_in_channel
+ssh_connector_set_in_fd
+ssh_connector_set_out_channel
+ssh_connector_set_out_fd
+ssh_copyright
+ssh_dirname
+ssh_disconnect
+ssh_dump_knownhost
+ssh_event_add_connector
+ssh_event_add_fd
+ssh_event_add_session
+ssh_event_dopoll
+ssh_event_free
+ssh_event_new
+ssh_event_remove_connector
+ssh_event_remove_fd
+ssh_event_remove_session
+ssh_execute_message_callbacks
+ssh_finalize
+ssh_forward_accept
+ssh_forward_cancel
+ssh_forward_listen
+ssh_free
+ssh_get_cipher_in
+ssh_get_cipher_out
+ssh_get_clientbanner
+ssh_get_disconnect_message
+ssh_get_error
+ssh_get_error_code
+ssh_get_fd
+ssh_get_fingerprint_hash
+ssh_get_hexa
+ssh_get_hmac_in
+ssh_get_hmac_out
+ssh_get_issue_banner
+ssh_get_kex_algo
+ssh_get_log_callback
+ssh_get_log_level
+ssh_get_log_userdata
+ssh_get_openssh_version
+ssh_get_poll_flags
+ssh_get_pubkey
+ssh_get_pubkey_hash
+ssh_get_publickey
+ssh_get_publickey_hash
+ssh_get_random
+ssh_get_server_publickey
+ssh_get_serverbanner
+ssh_get_status
+ssh_get_version
+ssh_getpass
+ssh_gssapi_get_creds
+ssh_gssapi_set_creds
+ssh_handle_key_exchange
+ssh_init
+ssh_is_blocking
+ssh_is_connected
+ssh_is_server_known
+ssh_key_cmp
+ssh_key_dup
+ssh_key_free
+ssh_key_is_private
+ssh_key_is_public
+ssh_key_new
+ssh_key_type
+ssh_key_type_from_name
+ssh_key_type_to_char
+ssh_known_hosts_parse_line
+ssh_knownhosts_entry_free
+ssh_log
+ssh_message_auth_interactive_request
+ssh_message_auth_kbdint_is_response
+ssh_message_auth_password
+ssh_message_auth_pubkey
+ssh_message_auth_publickey
+ssh_message_auth_publickey_state
+ssh_message_auth_reply_pk_ok
+ssh_message_auth_reply_pk_ok_simple
+ssh_message_auth_reply_success
+ssh_message_auth_set_methods
+ssh_message_auth_user
+ssh_message_channel_request_channel
+ssh_message_channel_request_command
+ssh_message_channel_request_env_name
+ssh_message_channel_request_env_value
+ssh_message_channel_request_open_destination
+ssh_message_channel_request_open_destination_port
+ssh_message_channel_request_open_originator
+ssh_message_channel_request_open_originator_port
+ssh_message_channel_request_open_reply_accept
+ssh_message_channel_request_open_reply_accept_channel
+ssh_message_channel_request_pty_height
+ssh_message_channel_request_pty_pxheight
+ssh_message_channel_request_pty_pxwidth
+ssh_message_channel_request_pty_term
+ssh_message_channel_request_pty_width
+ssh_message_channel_request_reply_success
+ssh_message_channel_request_subsystem
+ssh_message_channel_request_x11_auth_cookie
+ssh_message_channel_request_x11_auth_protocol
+ssh_message_channel_request_x11_screen_number
+ssh_message_channel_request_x11_single_connection
+ssh_message_free
+ssh_message_get
+ssh_message_global_request_address
+ssh_message_global_request_port
+ssh_message_global_request_reply_success
+ssh_message_reply_default
+ssh_message_retrieve
+ssh_message_service_reply_success
+ssh_message_service_service
+ssh_message_subtype
+ssh_message_type
+ssh_mkdir
+ssh_new
+ssh_options_copy
+ssh_options_get
+ssh_options_get_port
+ssh_options_getopt
+ssh_options_parse_config
+ssh_options_set
+ssh_pcap_file_close
+ssh_pcap_file_free
+ssh_pcap_file_new
+ssh_pcap_file_open
+ssh_pki_copy_cert_to_privkey
+ssh_pki_export_privkey_base64
+ssh_pki_export_privkey_file
+ssh_pki_export_privkey_to_pubkey
+ssh_pki_export_pubkey_base64
+ssh_pki_export_pubkey_file
+ssh_pki_generate
+ssh_pki_import_cert_base64
+ssh_pki_import_cert_file
+ssh_pki_import_privkey_base64
+ssh_pki_import_privkey_file
+ssh_pki_import_pubkey_base64
+ssh_pki_import_pubkey_file
+ssh_pki_key_ecdsa_name
+ssh_print_hash
+ssh_print_hexa
+ssh_privatekey_type
+ssh_publickey_to_file
+ssh_remove_channel_callbacks
+ssh_scp_accept_request
+ssh_scp_close
+ssh_scp_deny_request
+ssh_scp_free
+ssh_scp_init
+ssh_scp_leave_directory
+ssh_scp_new
+ssh_scp_pull_request
+ssh_scp_push_directory
+ssh_scp_push_file
+ssh_scp_push_file64
+ssh_scp_read
+ssh_scp_request_get_filename
+ssh_scp_request_get_permissions
+ssh_scp_request_get_size
+ssh_scp_request_get_size64
+ssh_scp_request_get_warning
+ssh_scp_write
+ssh_select
+ssh_send_debug
+ssh_send_ignore
+ssh_send_issue_banner
+ssh_send_keepalive
+ssh_server_init_kex
+ssh_service_request
+ssh_session_export_known_hosts_entry
+ssh_session_get_known_hosts_entry
+ssh_session_has_known_hosts_entry
+ssh_session_is_known_server
+ssh_session_set_disconnect_message
+ssh_session_update_known_hosts
+ssh_set_agent_channel
+ssh_set_agent_socket
+ssh_set_auth_methods
+ssh_set_blocking
+ssh_set_callbacks
+ssh_set_channel_callbacks
+ssh_set_counters
+ssh_set_fd_except
+ssh_set_fd_toread
+ssh_set_fd_towrite
+ssh_set_log_callback
+ssh_set_log_level
+ssh_set_log_userdata
+ssh_set_message_callback
+ssh_set_pcap_file
+ssh_set_server_callbacks
+ssh_silent_disconnect
+ssh_string_burn
+ssh_string_copy
+ssh_string_data
+ssh_string_fill
+ssh_string_free
+ssh_string_free_char
+ssh_string_from_char
+ssh_string_get_char
+ssh_string_len
+ssh_string_new
+ssh_string_to_char
+ssh_threads_get_default
+ssh_threads_get_noop
+ssh_threads_get_pthread
+ssh_threads_set_callbacks
+ssh_try_publickey_from_file
+ssh_userauth_agent
+ssh_userauth_agent_pubkey
+ssh_userauth_autopubkey
+ssh_userauth_gssapi
+ssh_userauth_kbdint
+ssh_userauth_kbdint_getanswer
+ssh_userauth_kbdint_getinstruction
+ssh_userauth_kbdint_getname
+ssh_userauth_kbdint_getnanswers
+ssh_userauth_kbdint_getnprompts
+ssh_userauth_kbdint_getprompt
+ssh_userauth_kbdint_setanswer
+ssh_userauth_list
+ssh_userauth_none
+ssh_userauth_offer_pubkey
+ssh_userauth_password
+ssh_userauth_privatekey_file
+ssh_userauth_pubkey
+ssh_userauth_publickey
+ssh_userauth_publickey_auto
+ssh_userauth_publickey_auto_get_current_identity
+ssh_userauth_try_publickey
+ssh_version
+ssh_vlog
+ssh_write_knownhost
+string_burn
+string_copy
+string_data
+string_fill
+string_free
+string_from_char
+string_len
+string_new
+string_to_char \ No newline at end of file
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 621f8b35..93ecb5e7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,6 @@
set(LIBSSH_PUBLIC_INCLUDE_DIRS ${libssh_SOURCE_DIR}/include)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${libssh_BINARY_DIR}/include
${libssh_BINARY_DIR}
)
@@ -9,24 +8,9 @@ set(LIBSSH_LINK_LIBRARIES
${LIBSSH_REQUIRED_LIBRARIES}
)
-if (WIN32)
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ws2_32
- )
-endif (WIN32)
-
-if (OPENSSL_CRYPTO_LIBRARY)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${OPENSSL_INCLUDE_DIR}
- )
-
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${OPENSSL_CRYPTO_LIBRARY}
- )
-endif (OPENSSL_CRYPTO_LIBRARY)
+if (TARGET OpenSSL::Crypto)
+ list(APPEND LIBSSH_LINK_LIBRARIES OpenSSL::Crypto)
+endif ()
if (MBEDTLS_CRYPTO_LIBRARY)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
@@ -51,15 +35,7 @@ if (GCRYPT_LIBRARIES)
endif()
if (WITH_ZLIB)
- set(LIBSSH_PRIVATE_INCLUDE_DIRS
- ${LIBSSH_PRIVATE_INCLUDE_DIRS}
- ${ZLIB_INCLUDE_DIR}
- )
-
- set(LIBSSH_LINK_LIBRARIES
- ${LIBSSH_LINK_LIBRARIES}
- ${ZLIB_LIBRARY}
- )
+ list(APPEND LIBSSH_LINK_LIBRARIES ZLIB::ZLIB)
endif (WITH_ZLIB)
if (WITH_GSSAPI AND GSSAPI_FOUND)
@@ -93,6 +69,16 @@ if (MINGW AND Threads_FOUND)
)
endif()
+# The ws2_32 needs to be last for mingw to build
+# https://gitlab.com/libssh/libssh-mirror/-/issues/84
+if (WIN32)
+ set(LIBSSH_LINK_LIBRARIES
+ ${LIBSSH_LINK_LIBRARIES}
+ iphlpapi
+ ws2_32
+ )
+endif (WIN32)
+
if (BUILD_STATIC_LIB)
set(LIBSSH_STATIC_LIBRARY
ssh_static
@@ -112,6 +98,7 @@ set(libssh_SRCS
config.c
connect.c
connector.c
+ crypto_common.c
curve25519.c
dh.c
ecdh.c
@@ -140,6 +127,7 @@ set(libssh_SRCS
socket.c
string.c
threads.c
+ ttyopts.c
wrapper.c
external/bcrypt_pbkdf.c
external/blowfish.c
@@ -181,6 +169,8 @@ if (WITH_GCRYPT)
gcrypt_missing.c
pki_gcrypt.c
ecdh_gcrypt.c
+ getrandom_gcrypt.c
+ md_gcrypt.c
dh_key.c
pki_ed25519.c
external/ed25519.c
@@ -204,52 +194,51 @@ elseif (WITH_MBEDTLS)
mbedcrypto_missing.c
pki_mbedcrypto.c
ecdh_mbedcrypto.c
+ getrandom_mbedcrypto.c
+ md_mbedcrypto.c
dh_key.c
pki_ed25519.c
external/ed25519.c
external/fe25519.c
external/ge25519.c
external/sc25519.c
- external/chacha.c
- external/poly1305.c
- chachapoly.c
)
+ if (NOT (HAVE_MBEDTLS_CHACHA20_H AND HAVE_MBEDTLS_POLY1305_H))
+ set(libssh_SRCS
+ ${libssh_SRCS}
+ external/chacha.c
+ external/poly1305.c
+ chachapoly.c
+ )
+ endif()
+
else (WITH_GCRYPT)
set(libssh_SRCS
${libssh_SRCS}
threads/libcrypto.c
pki_crypto.c
ecdh_crypto.c
+ getrandom_crypto.c
+ md_crypto.c
libcrypto.c
dh_crypto.c
)
- if (NOT HAVE_OPENSSL_ED25519)
- set(libssh_SRCS
- ${libssh_SRCS}
- pki_ed25519.c
- external/ed25519.c
- external/fe25519.c
- external/ge25519.c
- external/sc25519.c
- )
- endif (NOT HAVE_OPENSSL_ED25519)
- if (NOT (HAVE_OPENSSL_EVP_CHACHA20 AND HAVE_OPENSSL_EVP_POLY1305))
+ if (NOT HAVE_OPENSSL_EVP_CHACHA20)
set(libssh_SRCS
${libssh_SRCS}
external/chacha.c
external/poly1305.c
chachapoly.c
)
- endif (NOT (HAVE_OPENSSL_EVP_CHACHA20 AND HAVE_OPENSSL_EVP_POLY1305))
- if(OPENSSL_VERSION VERSION_LESS "1.1.0")
- set(libssh_SRCS ${libssh_SRCS} libcrypto-compat.c)
- endif()
+ endif (NOT HAVE_OPENSSL_EVP_CHACHA20)
endif (WITH_GCRYPT)
if (WITH_SFTP)
set(libssh_SRCS
${libssh_SRCS}
sftp.c
+ sftp_common.c
+ sftp_aio.c
)
if (WITH_SERVER)
@@ -291,12 +280,12 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
endif (WITH_GSSAPI AND GSSAPI_FOUND)
if (NOT WITH_NACL)
- if (NOT HAVE_OPENSSL_ED25519)
+ if (NOT HAVE_LIBCRYPTO)
set(libssh_SRCS
${libssh_SRCS}
external/curve25519_ref.c
)
- endif (NOT HAVE_OPENSSL_ED25519)
+ endif()
endif (NOT WITH_NACL)
# Set the path to the default map file
@@ -335,11 +324,14 @@ endif (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT AND ABIMAP_FOUND)
add_library(ssh ${libssh_SRCS})
target_compile_options(ssh
PRIVATE
- ${DEFAULT_C_COMPILE_FLAGS}
- -D_GNU_SOURCE)
+ ${DEFAULT_C_COMPILE_FLAGS})
+if (CYGWIN)
+ target_compile_definitions(ssh PRIVATE _GNU_SOURCE)
+endif ()
target_include_directories(ssh
PUBLIC
$<BUILD_INTERFACE:${libssh_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${libssh_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS})
@@ -347,7 +339,7 @@ target_link_libraries(ssh
PRIVATE ${LIBSSH_LINK_LIBRARIES})
if (WIN32 AND NOT BUILD_SHARED_LIBS)
- set_target_properties(ssh PROPERTIES COMPILE_FLAGS "-DLIBSSH_STATIC")
+ target_compile_definitions(ssh PUBLIC "LIBSSH_STATIC")
endif ()
add_library(ssh::ssh ALIAS ssh)
@@ -358,9 +350,7 @@ if (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT)
set(MAP_PATH "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_dev.map")
endif (ABIMAP_FOUND)
- set_target_properties(ssh
- PROPERTIES LINK_FLAGS
- "-Wl,--version-script,\"${MAP_PATH}\"")
+ target_link_libraries(ssh PRIVATE "-Wl,--version-script,\"${MAP_PATH}\"")
endif (WITH_SYMBOL_VERSIONING AND HAVE_LD_VERSION_SCRIPT)
set_target_properties(ssh
@@ -374,13 +364,17 @@ set_target_properties(ssh
)
if (WITH_VISIBILITY_HIDDEN)
- set_target_properties(ssh PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
+ set_target_properties(ssh PROPERTIES C_VISIBILITY_PRESET hidden)
endif (WITH_VISIBILITY_HIDDEN)
if (MINGW)
- set_target_properties(ssh PROPERTIES LINK_FLAGS "-Wl,--enable-stdcall-fixup")
- set_target_properties(ssh PROPERTIES COMPILE_FLAGS "-D_POSIX_SOURCE")
+ target_link_libraries(ssh PRIVATE "-Wl,--enable-stdcall-fixup")
+ target_compile_definitions(ssh PRIVATE "_POSIX_SOURCE")
endif ()
+if (WITH_COVERAGE)
+ include(CodeCoverage)
+ append_coverage_compiler_flags_to_target(ssh)
+endif (WITH_COVERAGE)
install(TARGETS ssh
@@ -397,12 +391,15 @@ if (BUILD_STATIC_LIB)
add_library(ssh-static STATIC ${libssh_SRCS})
target_compile_options(ssh-static
PRIVATE
- ${DEFAULT_C_COMPILE_FLAGS}
- -D_GNU_SOURCE)
+ ${DEFAULT_C_COMPILE_FLAGS})
+ if (CYGWIN)
+ target_compile_definitions(ssh-static PRIVATE _GNU_SOURCE)
+ endif ()
target_include_directories(ssh-static
PUBLIC
$<BUILD_INTERFACE:${libssh_SOURCE_DIR}/include>
+ $<BUILD_INTERFACE:${libssh_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE ${LIBSSH_PRIVATE_INCLUDE_DIRS})
target_link_libraries(ssh-static
@@ -428,13 +425,11 @@ if (BUILD_STATIC_LIB)
)
if (WIN32)
- set_target_properties(
- ssh-static
- PROPERTIES
- COMPILE_FLAGS
- "-DLIBSSH_STATIC"
- )
+ target_compile_definitions(ssh-static PUBLIC "LIBSSH_STATIC")
endif (WIN32)
+ if (WITH_COVERAGE)
+ append_coverage_compiler_flags_to_target(ssh-static)
+ endif (WITH_COVERAGE)
endif (BUILD_STATIC_LIB)
message(STATUS "Threads_FOUND=${Threads_FOUND}")
diff --git a/src/agent.c b/src/agent.c
index 62b0093e..1c79c6eb 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -33,8 +33,6 @@
* the agent returns the signed data
*/
-#ifndef _WIN32
-
#include "config.h"
#include <stdlib.h>
@@ -46,8 +44,14 @@
#include <unistd.h>
#endif
+#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
#include "libssh/agent.h"
#include "libssh/priv.h"
@@ -63,9 +67,9 @@
(((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \
((x) == SSH2_AGENT_FAILURE))
-static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int do_read) {
+static uint32_t atomicio(struct ssh_agent_struct *agent, void *buf, uint32_t n, int do_read) {
char *b = buf;
- size_t pos = 0;
+ uint32_t pos = 0;
ssize_t res;
ssh_pollfd_t pfd;
ssh_channel channel = agent->channel;
@@ -79,9 +83,9 @@ static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int
while (n > pos) {
if (do_read) {
- res = read(fd, b + pos, n - pos);
+ res = recv(fd, b + pos, n - pos, 0);
} else {
- res = write(fd, b + pos, n - pos);
+ res = send(fd, b + pos, n - pos, 0);
}
switch (res) {
case -1:
@@ -102,7 +106,7 @@ static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int
errno = do_read ? 0 : EPIPE;
return pos;
default:
- pos += (size_t) res;
+ pos += (uint32_t) res;
}
}
return pos;
@@ -117,7 +121,7 @@ static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int
continue;
if (res == SSH_ERROR)
return 0;
- pos += (size_t)res;
+ pos += (uint32_t)res;
}
return pos;
}
@@ -146,11 +150,21 @@ static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channe
agent->channel = channel;
}
+/**
+ * @addtogroup libssh_auth
+ *
+ * @{
+ */
+
/** @brief sets the SSH agent channel.
* The SSH agent channel will be used to authenticate this client using
* an agent through a channel, from another session. The most likely use
* is to implement SSH Agent forwarding into a SSH proxy.
+ *
+ * @param session the session
+ *
* @param[in] channel a SSH channel from another session.
+ *
* @returns SSH_OK in case of success
* SSH_ERROR in case of an error
*/
@@ -185,6 +199,10 @@ int ssh_set_agent_socket(ssh_session session, socket_t fd){
return SSH_OK;
}
+/**
+ * @}
+ */
+
void ssh_agent_close(struct ssh_agent_struct *agent) {
if (agent == NULL) {
return;
@@ -216,7 +234,8 @@ static int agent_connect(ssh_session session) {
if (session->agent->channel != NULL)
return 0;
- auth_sock = getenv("SSH_AUTH_SOCK");
+ auth_sock = session->opts.agent_socket ?
+ session->opts.agent_socket : getenv("SSH_AUTH_SOCK");
if (auth_sock && *auth_sock) {
if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) {
@@ -251,30 +270,32 @@ static int agent_decode_reply(struct ssh_session_struct *session, int type) {
static int agent_talk(struct ssh_session_struct *session,
struct ssh_buffer_struct *request, struct ssh_buffer_struct *reply) {
uint32_t len = 0;
- uint8_t payload[1024] = {0};
+ uint8_t tmpbuf[4];
+ uint8_t *payload = tmpbuf;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
len = ssh_buffer_get_len(request);
- SSH_LOG(SSH_LOG_TRACE, "Request length: %u", len);
+ SSH_LOG(SSH_LOG_TRACE, "Request length: %" PRIu32, len);
PUSH_BE_U32(payload, 0, len);
/* send length and then the request packet */
if (atomicio(session->agent, payload, 4, 0) == 4) {
if (atomicio(session->agent, ssh_buffer_get(request), len, 0)
!= len) {
- SSH_LOG(SSH_LOG_WARN, "atomicio sending request failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "atomicio sending request failed: %s",
strerror(errno));
return -1;
}
} else {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"atomicio sending request length failed: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
/* wait for response, read the length of the response packet */
if (atomicio(session->agent, payload, 4, 1) != 4) {
- SSH_LOG(SSH_LOG_WARN, "atomicio read response length failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "atomicio read response length failed: %s",
strerror(errno));
return -1;
}
@@ -282,26 +303,23 @@ static int agent_talk(struct ssh_session_struct *session,
len = PULL_BE_U32(payload, 0);
if (len > 256 * 1024) {
ssh_set_error(session, SSH_FATAL,
- "Authentication response too long: %u", len);
+ "Authentication response too long: %" PRIu32, len);
return -1;
}
- SSH_LOG(SSH_LOG_TRACE, "Response length: %u", len);
+ SSH_LOG(SSH_LOG_TRACE, "Response length: %" PRIu32, len);
- while (len > 0) {
- size_t n = len;
- if (n > sizeof(payload)) {
- n = sizeof(payload);
- }
- if (atomicio(session->agent, payload, n, 1) != n) {
- SSH_LOG(SSH_LOG_WARN,
- "Error reading response from authentication socket.");
- return -1;
- }
- if (ssh_buffer_add_data(reply, payload, n) < 0) {
- SSH_LOG(SSH_LOG_WARN, "Not enough space");
- return -1;
- }
- len -= n;
+ payload = ssh_buffer_allocate(reply, len);
+ if (payload == NULL) {
+ SSH_LOG(SSH_LOG_DEBUG, "Not enough space");
+ return -1;
+ }
+
+ if (atomicio(session->agent, payload, len, 1) != len) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Error reading response from authentication socket.");
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(reply, len);
+ return -1;
}
return 0;
@@ -313,7 +331,7 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
ssh_buffer reply = NULL;
unsigned int type = 0;
uint32_t count = 0;
- int rc;
+ uint32_t rc;
/* send message to the agent requesting the list of identities */
request = ssh_buffer_new();
@@ -345,7 +363,7 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
rc = ssh_buffer_get_u8(reply, (uint8_t *) &type);
if (rc != sizeof(uint8_t)) {
ssh_set_error(session, SSH_FATAL,
- "Bad authentication reply size: %d", rc);
+ "Bad authentication reply size: %" PRIu32, rc);
SSH_BUFFER_FREE(reply);
return 0;
}
@@ -353,7 +371,7 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
type = bswap_32(type);
#endif
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Answer type: %d, expected answer: %d",
type, SSH2_AGENT_IDENTITIES_ANSWER);
@@ -386,15 +404,13 @@ uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session)
return 0;
}
- if (session->agent->ident) {
- ssh_buffer_reinit(session->agent->ident);
- }
+ ssh_buffer_free(session->agent->ident);
session->agent->ident = reply;
return session->agent->count;
}
-/* caller has to free commment */
+/* caller has to free comment */
ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
char **comment) {
if (ssh_agent_get_ident_count(session) > 0) {
@@ -404,7 +420,7 @@ ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
return NULL;
}
-/* caller has to free commment */
+/* caller has to free comment */
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
char **comment) {
struct ssh_key_struct *key;
@@ -573,7 +589,7 @@ ssh_string ssh_agent_sign_data(ssh_session session,
#endif
if (agent_failed(type)) {
- SSH_LOG(SSH_LOG_WARN, "Agent reports failure in signing the key");
+ SSH_LOG(SSH_LOG_DEBUG, "Agent reports failure in signing the key");
SSH_BUFFER_FREE(reply);
return NULL;
} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
@@ -590,5 +606,3 @@ ssh_string ssh_agent_sign_data(ssh_session session,
return sig_blob;
}
-
-#endif /* _WIN32 */
diff --git a/src/auth.c b/src/auth.c
index 89272290..0acd9686 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -47,7 +47,7 @@
#include "libssh/legacy.h"
/**
- * @defgroup libssh_auth The SSH authentication functions.
+ * @defgroup libssh_auth The SSH authentication functions
* @ingroup libssh
*
* Functions to authenticate with a server.
@@ -58,7 +58,7 @@
/**
* @internal
*
- * @brief Ask access to the ssh-userauth service.
+ * @brief Ask for access to the ssh-userauth service.
*
* @param[in] session The SSH session handle.
*
@@ -66,19 +66,21 @@
* @returns SSH_AGAIN on nonblocking mode, if calling that function
* again is necessary
*/
-static int ssh_userauth_request_service(ssh_session session) {
+static int ssh_userauth_request_service(ssh_session session)
+{
int rc;
rc = ssh_service_request(session, "ssh-userauth");
if ((rc != SSH_OK) && (rc != SSH_AGAIN)) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to request \"ssh-userauth\" service");
}
return rc;
}
-static int ssh_auth_response_termination(void *user) {
+static int ssh_auth_response_termination(void *user)
+{
ssh_session session = (ssh_session)user;
switch (session->auth.state) {
case SSH_AUTH_STATE_NONE:
@@ -139,7 +141,8 @@ static const char *ssh_auth_get_current_method(ssh_session session)
* SSH_AUTH_AGAIN In nonblocking mode, call has to be made again
* SSH_AUTH_ERROR Error during the process.
*/
-static int ssh_userauth_get_response(ssh_session session) {
+static int ssh_userauth_get_response(ssh_session session)
+{
int rc = SSH_AUTH_ERROR;
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
@@ -199,7 +202,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_banner) {
banner = ssh_buffer_get_ssh_string(packet);
if (banner == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid SSH_USERAUTH_BANNER packet");
} else {
SSH_LOG(SSH_LOG_DEBUG,
@@ -237,7 +240,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
if (partial) {
session->auth.state = SSH_AUTH_STATE_PARTIAL;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Partial success for '%s'. Authentication that can continue: %s",
current_method,
auth_methods);
@@ -247,7 +250,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
"Access denied for '%s'. Authentication that can continue: %s",
current_method,
auth_methods);
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"%s",
ssh_get_error(session));
@@ -364,7 +367,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_pk_ok) {
*
* @param[in] username Deprecated, set to NULL.
*
- * @returns A bitfield of the fllowing values:
+ * @returns A bitfield of the following values:
* - SSH_AUTH_METHOD_PASSWORD
* - SSH_AUTH_METHOD_PUBLICKEY
* - SSH_AUTH_METHOD_HOSTBASED
@@ -403,10 +406,11 @@ int ssh_userauth_list(ssh_session session, const char *username)
* authentication. The username should only be set with ssh_options_set() only
* before you connect to the server.
*/
-int ssh_userauth_none(ssh_session session, const char *username) {
+int ssh_userauth_none(ssh_session session, const char *username)
+{
int rc;
- switch(session->pending_call_state) {
+ switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_NONE:
@@ -492,6 +496,7 @@ int ssh_userauth_try_publickey(ssh_session session,
{
ssh_string pubkey_s = NULL;
const char *sig_type_c = NULL;
+ bool allowed;
int rc;
if (session == NULL) {
@@ -513,7 +518,7 @@ int ssh_userauth_try_publickey(ssh_session session,
SSH_FATAL,
"Wrong state (%d) during pending SSH call",
session->pending_call_state);
- return SSH_ERROR;
+ return SSH_AUTH_ERROR;
}
/* Check if the given public key algorithm is allowed */
@@ -523,13 +528,21 @@ int ssh_userauth_try_publickey(ssh_session session,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, pubkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ return SSH_AUTH_DENIED;
+ }
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@@ -544,6 +557,7 @@ int ssh_userauth_try_publickey(ssh_session session,
goto fail;
}
+ SSH_LOG(SSH_LOG_TRACE, "Trying signature type %s", sig_type_c);
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
SSH2_MSG_USERAUTH_REQUEST,
@@ -611,6 +625,7 @@ int ssh_userauth_publickey(ssh_session session,
const ssh_key privkey)
{
ssh_string str = NULL;
+ bool allowed;
int rc;
const char *sig_type_c = NULL;
enum ssh_keytypes_e key_type;
@@ -647,13 +662,21 @@ int ssh_userauth_publickey(ssh_session session,
"Invalid key type (unknown)");
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
sig_type_c);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, privkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(privkey));
+ return SSH_AUTH_DENIED;
+ }
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
@@ -668,6 +691,7 @@ int ssh_userauth_publickey(ssh_session session,
goto fail;
}
+ SSH_LOG(SSH_LOG_TRACE, "Sending signature type %s", sig_type_c);
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
SSH2_MSG_USERAUTH_REQUEST,
@@ -722,7 +746,6 @@ fail:
return SSH_AUTH_ERROR;
}
-#ifndef _WIN32
static int ssh_userauth_agent_publickey(ssh_session session,
const char *username,
ssh_key pubkey)
@@ -730,18 +753,20 @@ static int ssh_userauth_agent_publickey(ssh_session session,
ssh_string pubkey_s = NULL;
ssh_string sig_blob = NULL;
const char *sig_type_c = NULL;
+ bool allowed;
int rc;
- switch(session->pending_call_state) {
- case SSH_PENDING_CALL_NONE:
- break;
- case SSH_PENDING_CALL_AUTH_AGENT:
- goto pending;
- default:
- ssh_set_error(session,
- SSH_FATAL,
- "Bad call during pending SSH call in ssh_userauth_try_publickey");
- return SSH_ERROR;
+ switch (session->pending_call_state) {
+ case SSH_PENDING_CALL_NONE:
+ break;
+ case SSH_PENDING_CALL_AUTH_AGENT:
+ goto pending;
+ default:
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Bad call during pending SSH call in %s",
+ __func__);
+ return SSH_ERROR;
}
rc = ssh_userauth_request_service(session);
@@ -765,7 +790,8 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
- if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+ rc = ssh_key_algorithm_allowed(session, sig_type_c);
+ if (!rc) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"The key algorithm '%s' is not allowed to be used by"
" PUBLICKEY_ACCEPTED_TYPES configuration option",
@@ -773,17 +799,25 @@ static int ssh_userauth_agent_publickey(ssh_session session,
SSH_STRING_FREE(pubkey_s);
return SSH_AUTH_DENIED;
}
+ allowed = ssh_key_size_allowed(session, pubkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The '%s' key type of size %d is not allowed by "
+ "RSA_MIN_SIZE", sig_type_c, ssh_key_size(pubkey));
+ SSH_STRING_FREE(pubkey_s);
+ return SSH_AUTH_DENIED;
+ }
/* request */
rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
- SSH2_MSG_USERAUTH_REQUEST,
- username ? username : session->opts.username,
- "ssh-connection",
- "publickey",
- 1, /* private key */
- sig_type_c, /* algo */
- pubkey_s /* public key */
- );
+ SSH2_MSG_USERAUTH_REQUEST,
+ username ? username : session->opts.username,
+ "ssh-connection",
+ "publickey",
+ 1, /* private key */
+ sig_type_c, /* algo */
+ pubkey_s /* public key */
+ );
SSH_STRING_FREE(pubkey_s);
if (rc < 0) {
goto fail;
@@ -827,6 +861,7 @@ fail:
enum ssh_agent_state_e {
SSH_AGENT_STATE_NONE = 0,
SSH_AGENT_STATE_PUBKEY,
+ SSH_AGENT_STATE_CERT,
SSH_AGENT_STATE_AUTH
};
@@ -837,7 +872,8 @@ struct ssh_agent_state_struct {
};
/* Internal function */
-void ssh_agent_state_free(void *data) {
+void ssh_agent_state_free(void *data)
+{
struct ssh_agent_state_struct *state = data;
if (state) {
@@ -870,9 +906,15 @@ void ssh_agent_state_free(void *data) {
* before you connect to the server.
*/
int ssh_userauth_agent(ssh_session session,
- const char *username) {
+ const char *username)
+{
int rc = SSH_AUTH_ERROR;
- struct ssh_agent_state_struct *state;
+ struct ssh_agent_state_struct *state = NULL;
+ ssh_key *configKeys = NULL;
+ ssh_key *configCerts = NULL;
+ size_t configKeysCount = 0;
+ size_t configCertsCount = 0;
+ size_t i;
if (session == NULL) {
return SSH_AUTH_ERROR;
@@ -889,7 +931,7 @@ int ssh_userauth_agent(ssh_session session,
return SSH_AUTH_ERROR;
}
ZERO_STRUCTP(session->agent_state);
- session->agent_state->state=SSH_AGENT_STATE_NONE;
+ session->agent_state->state = SSH_AGENT_STATE_NONE;
}
state = session->agent_state;
@@ -901,70 +943,239 @@ int ssh_userauth_agent(ssh_session session,
return SSH_AUTH_DENIED;
}
+ if (session->opts.identities_only) {
+ /*
+ * Read keys mentioned in the config, so we can check if key from agent
+ * is in there.
+ */
+ size_t identityLen = ssh_list_count(session->opts.identity);
+ size_t certsLen = ssh_list_count(session->opts.certificate);
+ struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
+
+ configKeys = malloc(identityLen * sizeof(ssh_key));
+ configCerts = malloc((certsLen + identityLen) * sizeof(ssh_key));
+ if (configKeys == NULL || configCerts == NULL) {
+ free(configKeys);
+ free(configCerts);
+ ssh_set_error_oom(session);
+ return SSH_AUTH_ERROR;
+ }
+
+ while (it != NULL && configKeysCount < identityLen) {
+ const char *privkeyFile = it->data;
+ size_t certPathLen;
+ char *certFile = NULL;
+ ssh_key pubkey = NULL;
+ ssh_key cert = NULL;
+
+ /*
+ * Read the private key file listed in the config, but we're only
+ * interested in the public key. Don't try to decrypt private key.
+ */
+ rc = ssh_pki_import_pubkey_file(privkeyFile, &pubkey);
+ if (rc == SSH_OK) {
+ configKeys[configKeysCount++] = pubkey;
+ } else {
+ char *pubkeyFile = NULL;
+ size_t pubkeyPathLen = strlen(privkeyFile) + sizeof(".pub");
+
+ SSH_KEY_FREE(pubkey);
+
+ /*
+ * If we couldn't get the public key from the private key file,
+ * try a .pub file instead.
+ */
+ pubkeyFile = malloc(pubkeyPathLen);
+ if (!pubkeyFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(pubkeyFile, pubkeyPathLen, "%s.pub", privkeyFile);
+ rc = ssh_pki_import_pubkey_file(pubkeyFile, &pubkey);
+ free(pubkeyFile);
+ if (rc == SSH_OK) {
+ configKeys[configKeysCount++] = pubkey;
+ } else if (pubkey) {
+ SSH_KEY_FREE(pubkey);
+ }
+ }
+ /* Now try to see if there is a certificate with default name
+ * do not merge it yet with the key as we need to try first the
+ * non-certified key */
+ certPathLen = strlen(privkeyFile) + sizeof("-cert.pub");
+ certFile = malloc(certPathLen);
+ if (!certFile) {
+ ssh_set_error_oom(session);
+ rc = SSH_AUTH_ERROR;
+ goto done;
+ }
+ snprintf(certFile, certPathLen, "%s-cert.pub", privkeyFile);
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ free(certFile);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ /* And now load separately-listed certificates. */
+ it = ssh_list_get_iterator(session->opts.certificate);
+ while (it != NULL && configCertsCount < certsLen + identityLen) {
+ const char *certFile = it->data;
+ ssh_key cert = NULL;
+
+ rc = ssh_pki_import_cert_file(certFile, &cert);
+ if (rc == SSH_OK) {
+ configCerts[configCertsCount++] = cert;
+ } else if (cert) {
+ SSH_KEY_FREE(cert);
+ }
+
+ it = it->next;
+ }
+ }
+
while (state->pubkey != NULL) {
if (state->state == SSH_AGENT_STATE_NONE) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying identity %s", state->comment);
+ "Trying identity %s",
+ state->comment);
+ if (session->opts.identities_only) {
+ /* Check if this key is one of the keys listed in the config */
+ bool found_key = false;
+ for (i = 0; i < configKeysCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configKeys[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+ /* or in separate certificates */
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ found_key = true;
+ break;
+ }
+ }
+
+ if (!found_key) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Identities only is enabled and identity %s was "
+ "not listed in config, skipping",
+ state->comment);
+ SSH_STRING_FREE_CHAR(state->comment);
+ state->comment = NULL;
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_agent_get_next_ident(
+ session, &state->comment);
+
+ if (state->pubkey == NULL) {
+ rc = SSH_AUTH_DENIED;
+ }
+ continue;
+ }
+ }
}
if (state->state == SSH_AGENT_STATE_NONE ||
- state->state == SSH_AGENT_STATE_PUBKEY) {
+ state->state == SSH_AGENT_STATE_PUBKEY ||
+ state->state == SSH_AGENT_STATE_CERT) {
rc = ssh_userauth_try_publickey(session, username, state->pubkey);
if (rc == SSH_AUTH_ERROR) {
- ssh_agent_state_free (state);
+ ssh_agent_state_free(state);
session->agent_state = NULL;
- return rc;
+ goto done;
} else if (rc == SSH_AUTH_AGAIN) {
- state->state = SSH_AGENT_STATE_PUBKEY;
- return rc;
+ state->state = (state->state == SSH_AGENT_STATE_NONE ?
+ SSH_AGENT_STATE_PUBKEY : state->state);
+ goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s refused by server", state->comment);
+ "Public key of %s refused by server",
+ state->comment);
+ if (state->state == SSH_AGENT_STATE_PUBKEY) {
+ for (i = 0; i < configCertsCount; i++) {
+ int cmp = ssh_key_cmp(state->pubkey,
+ configCerts[i],
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Retry with matching certificate");
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_key_dup(configCerts[i]);
+ state->state = SSH_AGENT_STATE_CERT;
+ continue;
+ }
+ }
+ }
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_agent_get_next_ident(session,
+ &state->comment);
state->state = SSH_AGENT_STATE_NONE;
continue;
}
SSH_LOG(SSH_LOG_DEBUG,
- "Public key of %s accepted by server", state->comment);
+ "Public key of %s accepted by server",
+ state->comment);
state->state = SSH_AGENT_STATE_AUTH;
}
if (state->state == SSH_AGENT_STATE_AUTH) {
rc = ssh_userauth_agent_publickey(session, username, state->pubkey);
- if (rc == SSH_AUTH_AGAIN)
- return rc;
+ if (rc == SSH_AUTH_AGAIN) {
+ goto done;
+ }
SSH_STRING_FREE_CHAR(state->comment);
state->comment = NULL;
if (rc == SSH_AUTH_ERROR || rc == SSH_AUTH_PARTIAL) {
- ssh_agent_state_free (session->agent_state);
+ ssh_agent_state_free(session->agent_state);
session->agent_state = NULL;
- return rc;
+ goto done;
} else if (rc != SSH_AUTH_SUCCESS) {
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Server accepted public key but refused the signature");
- ssh_key_free(state->pubkey);
- state->pubkey = ssh_agent_get_next_ident(session, &state->comment);
+ SSH_KEY_FREE(state->pubkey);
+ state->pubkey = ssh_agent_get_next_ident(session,
+ &state->comment);
state->state = SSH_AGENT_STATE_NONE;
continue;
}
ssh_agent_state_free (session->agent_state);
session->agent_state = NULL;
- return SSH_AUTH_SUCCESS;
+ rc = SSH_AUTH_SUCCESS;
+ goto done;
}
}
ssh_agent_state_free (session->agent_state);
session->agent_state = NULL;
+done:
+ for (i = 0; i < configKeysCount; i++) {
+ ssh_key_free(configKeys[i]);
+ }
+ free(configKeys);
+ for (i = 0; i < configCertsCount; i++) {
+ ssh_key_free(configCerts[i]);
+ }
+ free(configCerts);
return rc;
}
-#endif
enum ssh_auth_auto_state_e {
SSH_AUTH_AUTO_STATE_NONE = 0,
SSH_AUTH_AUTO_STATE_PUBKEY,
SSH_AUTH_AUTO_STATE_KEY_IMPORTED,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE,
+ SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION,
SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED
};
@@ -973,9 +1184,61 @@ struct ssh_auth_auto_state_struct {
struct ssh_iterator *it;
ssh_key privkey;
ssh_key pubkey;
+ ssh_key cert;
+ struct ssh_iterator *cert_it;
};
/**
+ * @brief Get the identity that is currently being processed by
+ * ssh_userauth_publickey_auto()
+ *
+ * This is meant to be used by a callback that happens as part of the
+ * execution of ssh_userauth_publickey_auto(). The auth_function
+ * callback might want to know which key a passphrase is needed for,
+ * for example.
+ *
+ * @param[in] session The SSH session.
+ *
+ * @param[out] value The value to get into. As a char**, space will be
+ * allocated by the function for the value, it is
+ * your responsibility to free the memory using
+ * ssh_string_free_char().
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+int ssh_userauth_publickey_auto_get_current_identity(ssh_session session,
+ char** value)
+{
+ const char *id = NULL;
+
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return SSH_ERROR;
+ }
+
+ if (session->auth.auto_state != NULL &&
+ session->auth.auto_state->it != NULL) {
+ id = session->auth.auto_state->it->data;
+ }
+
+ if (id == NULL) {
+ return SSH_ERROR;
+ }
+
+ *value = strdup(id);
+ if (*value == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+/**
* @brief Tries to automatically authenticate with public key and "none"
*
* It may fail, for instance it doesn't ask for a password and uses a default
@@ -995,14 +1258,16 @@ struct ssh_auth_auto_state_struct {
* method.\n
* SSH_AUTH_PARTIAL: You've been partially authenticated, you still
* have to use another method.\n
- * SSH_AUTH_SUCCESS: The public key is accepted, you want now to use
- * ssh_userauth_publickey().\n
+ * SSH_AUTH_SUCCESS: Authentication success\n
* SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again
* later.
*
* @note Most server implementations do not permit changing the username during
* authentication. The username should only be set with ssh_options_set() only
* before you connect to the server.
+ *
+ * The OpenSSH iterates over the identities and first try the plain public key
+ * and then the certificate if it is in place.
*/
int ssh_userauth_publickey_auto(ssh_session session,
const char *username,
@@ -1010,7 +1275,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
{
ssh_auth_callback auth_fn = NULL;
void *auth_data = NULL;
- struct ssh_auth_auto_state_struct *state;
+ struct ssh_auth_auto_state_struct *state = NULL;
int rc;
if (session == NULL) {
@@ -1037,15 +1302,13 @@ int ssh_userauth_publickey_auto(ssh_session session,
}
state = session->auth.auto_state;
if (state->state == SSH_AUTH_AUTO_STATE_NONE) {
-#ifndef _WIN32
/* Try authentication with ssh-agent first */
rc = ssh_userauth_agent(session, username);
if (rc == SSH_AUTH_SUCCESS ||
rc == SSH_AUTH_PARTIAL ||
- rc == SSH_AUTH_AGAIN ) {
+ rc == SSH_AUTH_AGAIN) {
return rc;
}
-#endif
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
}
if (state->it == NULL) {
@@ -1054,14 +1317,17 @@ int ssh_userauth_publickey_auto(ssh_session session,
while (state->it != NULL) {
const char *privkey_file = state->it->data;
- char pubkey_file[1024] = {0};
+ char pubkey_file[PATH_MAX] = {0};
if (state->state == SSH_AUTH_AUTO_STATE_PUBKEY) {
SSH_LOG(SSH_LOG_DEBUG,
- "Trying to authenticate with %s", privkey_file);
+ "Trying to authenticate with %s",
+ privkey_file);
+ state->cert = NULL;
state->privkey = NULL;
state->pubkey = NULL;
+#ifdef WITH_PKCS11_URI
if (ssh_pki_is_uri(privkey_file)) {
char *pub_uri_from_priv = NULL;
SSH_LOG(SSH_LOG_INFO,
@@ -1070,84 +1336,165 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (pub_uri_from_priv == NULL) {
return SSH_ERROR;
} else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s",
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s",
pub_uri_from_priv);
SAFE_FREE(pub_uri_from_priv);
}
- } else {
- snprintf(pubkey_file, sizeof(pubkey_file), "%s.pub", privkey_file);
+ } else
+#endif /* WITH_PKCS11_URI */
+ {
+ snprintf(pubkey_file,
+ sizeof(pubkey_file),
+ "%s.pub",
+ privkey_file);
}
rc = ssh_pki_import_pubkey_file(pubkey_file, &state->pubkey);
if (rc == SSH_ERROR) {
ssh_set_error(session,
- SSH_FATAL,
- "Failed to import public key: %s",
- pubkey_file);
+ SSH_FATAL,
+ "Failed to import public key: %s",
+ pubkey_file);
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_ERROR;
} else if (rc == SSH_EOF) {
/* Read the private key and save the public key to file */
rc = ssh_pki_import_privkey_file(privkey_file,
- passphrase,
- auth_fn,
- auth_data,
- &state->privkey);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ SSH_FATAL,
+ "Failed to read private key: %s",
+ privkey_file);
+ state->it = state->it->next;
continue;
} else if (rc == SSH_EOF) {
/* If the file doesn't exist, continue */
SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
- state->it=state->it->next;
+ state->it = state->it->next;
continue;
}
- rc = ssh_pki_export_privkey_to_pubkey(state->privkey, &state->pubkey);
+ rc = ssh_pki_export_privkey_to_pubkey(state->privkey,
+ &state->pubkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->privkey);
+ SSH_KEY_FREE(state->privkey);
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_ERROR;
}
rc = ssh_pki_export_pubkey_file(state->pubkey, pubkey_file);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not write public key to file: %s",
pubkey_file);
}
}
state->state = SSH_AUTH_AUTO_STATE_KEY_IMPORTED;
}
- if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
- rc = ssh_userauth_try_publickey(session, username, state->pubkey);
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE ||
+ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ ssh_key k = state->pubkey;
+ if (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ k = state->cert;
+ }
+ rc = ssh_userauth_try_publickey(session, username, k);
if (rc == SSH_AUTH_ERROR) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Public key authentication error for %s",
privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
return rc;
} else if (rc == SSH_AUTH_AGAIN) {
return rc;
} else if (rc != SSH_AUTH_SUCCESS) {
+ int r; /* do not reuse `rc` as it is used to return from here */
+ SSH_KEY_FREE(state->cert);
SSH_LOG(SSH_LOG_DEBUG,
- "Public key for %s refused by server",
- privkey_file);
- ssh_key_free(state->privkey);
- state->privkey = NULL;
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
- state->it=state->it->next;
+ "Public key for %s%s refused by server",
+ privkey_file,
+ (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED
+ ? " (with certificate)" : ""));
+ /* Try certificate file by appending -cert.pub (if present) */
+ if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED) {
+ char cert_file[PATH_MAX] = {0};
+ ssh_key cert = NULL;
+
+ snprintf(cert_file,
+ sizeof(cert_file),
+ "%s-cert.pub",
+ privkey_file);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (default path)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ /* TODO check the pubkey and certs match */
+ SSH_LOG(SSH_LOG_TRACE,
+ "Certificate loaded %s. Retry the authentication.",
+ cert_file);
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE;
+ SSH_KEY_FREE(state->cert);
+ state->cert = cert;
+ /* try to authenticate with this certificate */
+ continue;
+ }
+ /* if the file does not exists, try configuration options */
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ }
+ /* Try certificate files loaded through options */
+ if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) {
+ SSH_KEY_FREE(state->cert);
+ if (state->cert_it == NULL) {
+ state->cert_it = ssh_list_get_iterator(session->opts.certificate);
+ }
+ while (state->cert_it != NULL) {
+ const char *cert_file = state->cert_it->data;
+ ssh_key cert = NULL;
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Trying to load the certificate %s (options)",
+ cert_file);
+ r = ssh_pki_import_cert_file(cert_file, &cert);
+ if (r == SSH_OK) {
+ int cmp = ssh_key_cmp(cert,
+ state->pubkey,
+ SSH_KEY_CMP_PUBLIC);
+ if (cmp != 0) {
+ state->cert_it = state->cert_it->next;
+ SSH_KEY_FREE(cert);
+ continue; /* with next cert */
+ }
+ SSH_LOG(SSH_LOG_TRACE,
+ "Found matching certificate %s in options. Retry the authentication.",
+ cert_file);
+ state->cert = cert;
+ cert = NULL;
+ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION;
+ /* try to authenticate with this identity */
+ break; /* try this cert */
+ }
+ /* continue with next identity */
+ }
+ if (state->cert != NULL) {
+ continue; /* retry with the certificate */
+ }
+ }
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
}
@@ -1157,25 +1504,25 @@ int ssh_userauth_publickey_auto(ssh_session session,
/* Public key has been accepted by the server */
if (state->privkey == NULL) {
rc = ssh_pki_import_privkey_file(privkey_file,
- passphrase,
- auth_fn,
- auth_data,
- &state->privkey);
+ passphrase,
+ auth_fn,
+ auth_data,
+ &state->privkey);
if (rc == SSH_ERROR) {
- ssh_key_free(state->pubkey);
- state->pubkey=NULL;
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
ssh_set_error(session,
- SSH_FATAL,
- "Failed to read private key: %s",
- privkey_file);
- state->it=state->it->next;
+ SSH_FATAL,
+ "Failed to read private key: %s",
+ privkey_file);
+ state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
continue;
} else if (rc == SSH_EOF) {
/* If the file doesn't exist, continue */
- ssh_key_free(state->pubkey);
- state->pubkey = NULL;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->pubkey);
+ SSH_LOG(SSH_LOG_DEBUG,
"Private key %s doesn't exist.",
privkey_file);
state->it = state->it->next;
@@ -1183,16 +1530,33 @@ int ssh_userauth_publickey_auto(ssh_session session,
continue;
}
}
+ if (state->cert != NULL && !is_cert_type(state->privkey->cert_type)) {
+ rc = ssh_pki_copy_cert_to_privkey(state->cert, state->privkey);
+ if (rc != SSH_OK) {
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to copy cert to private key");
+ state->it = state->it->next;
+ state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
+ continue;
+ }
+ }
rc = ssh_userauth_publickey(session, username, state->privkey);
if (rc != SSH_AUTH_AGAIN && rc != SSH_AUTH_DENIED) {
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ bool cert_used = (state->cert != NULL);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
SAFE_FREE(session->auth.auto_state);
if (rc == SSH_AUTH_SUCCESS) {
- SSH_LOG(SSH_LOG_INFO,
- "Successfully authenticated using %s",
- privkey_file);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Successfully authenticated using %s%s",
+ privkey_file,
+ (cert_used ? " and certificate" : ""));
}
return rc;
}
@@ -1200,18 +1564,19 @@ int ssh_userauth_publickey_auto(ssh_session session,
return rc;
}
- ssh_key_free(state->privkey);
- ssh_key_free(state->pubkey);
+ SSH_KEY_FREE(state->cert);
+ SSH_KEY_FREE(state->privkey);
+ SSH_KEY_FREE(state->pubkey);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_DEBUG,
"The server accepted the public key but refused the signature");
state->it = state->it->next;
state->state = SSH_AUTH_AUTO_STATE_PUBKEY;
/* continue */
}
}
- SSH_LOG(SSH_LOG_INFO,
- "Tried every public key, none matched");
+ SSH_LOG(SSH_LOG_WARN,
+ "Access denied: Tried every public key, none matched");
SAFE_FREE(session->auth.auto_state);
return SSH_AUTH_DENIED;
}
@@ -1250,10 +1615,11 @@ int ssh_userauth_publickey_auto(ssh_session session,
*/
int ssh_userauth_password(ssh_session session,
const char *username,
- const char *password) {
+ const char *password)
+{
int rc;
- switch(session->pending_call_state) {
+ switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_PASSWORD:
@@ -1311,7 +1677,6 @@ fail:
return SSH_AUTH_ERROR;
}
-#ifndef _WIN32
/* LEGACY */
int ssh_userauth_agent_pubkey(ssh_session session,
const char *username,
@@ -1328,20 +1693,26 @@ int ssh_userauth_agent_pubkey(ssh_session session,
key->type = publickey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PUBLIC;
- key->dsa = publickey->dsa_pub;
+#ifndef HAVE_LIBCRYPTO
key->rsa = publickey->rsa_pub;
+#else
+ key->key = publickey->key_pub;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_userauth_agent_publickey(session, username, key);
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return rc;
}
-#endif /* _WIN32 */
-ssh_kbdint ssh_kbdint_new(void) {
+ssh_kbdint ssh_kbdint_new(void)
+{
ssh_kbdint kbd;
kbd = calloc(1, sizeof(struct ssh_kbdint_struct));
@@ -1353,7 +1724,8 @@ ssh_kbdint ssh_kbdint_new(void) {
}
-void ssh_kbdint_free(ssh_kbdint kbd) {
+void ssh_kbdint_free(ssh_kbdint kbd)
+{
size_t i, n;
if (kbd == NULL) {
@@ -1389,7 +1761,8 @@ void ssh_kbdint_free(ssh_kbdint kbd) {
SAFE_FREE(kbd);
}
-void ssh_kbdint_clean(ssh_kbdint kbd) {
+void ssh_kbdint_clean(ssh_kbdint kbd)
+{
size_t i, n;
if (kbd == NULL) {
@@ -1589,10 +1962,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
}
SSH_LOG(SSH_LOG_DEBUG,
- "%d keyboard-interactive prompts", nprompts);
+ "%" PRIu32 " keyboard-interactive prompts", nprompts);
if (nprompts > KBDINT_MAX_PROMPT) {
ssh_set_error(session, SSH_FATAL,
- "Too much prompts requested by the server: %u (0x%.4x)",
+ "Too much prompts requested by the server: %" PRIu32 " (0x%.4" PRIx32 ")",
nprompts, nprompts);
ssh_kbdint_free(session->kbdint);
session->kbdint = NULL;
@@ -1667,7 +2040,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_request) {
* @see ssh_userauth_kbdint_setanswer()
*/
int ssh_userauth_kbdint(ssh_session session, const char *user,
- const char *submethods) {
+ const char *submethods)
+{
int rc = SSH_AUTH_ERROR;
if (session == NULL) {
@@ -1709,7 +2083,8 @@ int ssh_userauth_kbdint(ssh_session session, const char *user,
*
* @returns The number of prompts.
*/
-int ssh_userauth_kbdint_getnprompts(ssh_session session) {
+int ssh_userauth_kbdint_getnprompts(ssh_session session)
+{
if (session == NULL) {
return SSH_ERROR;
}
@@ -1731,7 +2106,8 @@ int ssh_userauth_kbdint_getnprompts(ssh_session session) {
*
* @returns The name of the message block. Do not free it.
*/
-const char *ssh_userauth_kbdint_getname(ssh_session session) {
+const char *ssh_userauth_kbdint_getname(ssh_session session)
+{
if (session == NULL) {
return NULL;
}
@@ -1754,7 +2130,8 @@ const char *ssh_userauth_kbdint_getname(ssh_session session) {
* @returns The instruction of the message block.
*/
-const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
+const char *ssh_userauth_kbdint_getinstruction(ssh_session session)
+{
if (session == NULL)
return NULL;
if (session->kbdint == NULL) {
@@ -1789,8 +2166,9 @@ const char *ssh_userauth_kbdint_getinstruction(ssh_session session) {
* if (echo) ...
* @endcode
*/
-const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
- char *echo) {
+const char *
+ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i, char *echo)
+{
if (session == NULL)
return NULL;
if (session->kbdint == NULL) {
@@ -1817,7 +2195,8 @@ const char *ssh_userauth_kbdint_getprompt(ssh_session session, unsigned int i,
*
* @returns The number of answers.
*/
-int ssh_userauth_kbdint_getnanswers(ssh_session session) {
+int ssh_userauth_kbdint_getnanswers(ssh_session session)
+{
if (session == NULL || session->kbdint == NULL) {
return SSH_ERROR;
}
@@ -1825,15 +2204,17 @@ int ssh_userauth_kbdint_getnanswers(ssh_session session) {
}
/**
- * @brief Get the answer for a question from a message block.
+ * @brief Get the answer to a question from a message block.
*
* @param[in] session The ssh session to use.
*
* @param[in] i index The number of the ith answer.
*
- * @return 0 on success, < 0 on error.
+ * @return The answer string, or NULL if the answer is not
+ * available. Do not free the string.
*/
-const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
+const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i)
+{
if (session == NULL || session->kbdint == NULL
|| session->kbdint->answers == NULL) {
return NULL;
@@ -1864,8 +2245,10 @@ const char *ssh_userauth_kbdint_getanswer(ssh_session session, unsigned int i) {
*
* @return 0 on success, < 0 on error.
*/
-int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
- const char *answer) {
+int
+ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
+ const char *answer)
+{
if (session == NULL) {
return -1;
}
@@ -1911,7 +2294,8 @@ int ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
* SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again
* later.
*/
-int ssh_userauth_gssapi(ssh_session session) {
+int ssh_userauth_gssapi(ssh_session session)
+{
int rc = SSH_AUTH_DENIED;
#ifdef WITH_GSSAPI
switch(session->pending_call_state) {
@@ -1933,7 +2317,7 @@ int ssh_userauth_gssapi(ssh_session session) {
} else if (rc == SSH_ERROR) {
return SSH_AUTH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi-with-mic");
+ SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-with-mic");
session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_MIC;
session->auth.state = SSH_AUTH_STATE_NONE;
diff --git a/src/bignum.c b/src/bignum.c
index ef8de31f..72429460 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -44,7 +44,7 @@ ssh_string ssh_make_bignum_string(bignum num) {
#ifdef DEBUG_CRYPTO
SSH_LOG(SSH_LOG_TRACE,
- "%zu bits, %zu bytes, %zu padding\n",
+ "%zu bits, %zu bytes, %zu padding",
bits, len, pad);
#endif /* DEBUG_CRYPTO */
@@ -70,7 +70,7 @@ bignum ssh_make_string_bn(ssh_string string)
#ifdef DEBUG_CRYPTO
SSH_LOG(SSH_LOG_TRACE,
- "Importing a %zu bits, %zu bytes object ...\n",
+ "Importing a %zu bits, %zu bytes object ...",
len * 8, len);
#endif /* DEBUG_CRYPTO */
@@ -86,12 +86,7 @@ void ssh_print_bignum(const char *name, const_bignum num)
if (num != NULL) {
bignum_bn2hex(num, &hex);
}
- fprintf(stderr, "%s value: %s\n", name, (hex == NULL) ? "(null)" : (char *) hex);
-#ifdef HAVE_LIBGCRYPT
- SAFE_FREE(hex);
-#elif defined HAVE_LIBCRYPTO
- OPENSSL_free(hex);
-#elif defined HAVE_LIBMBEDCRYPTO
- SAFE_FREE(hex);
-#endif
+ SSH_LOG(SSH_LOG_DEBUG, "%s value: %s", name,
+ (hex == NULL) ? "(null)" : (char *)hex);
+ ssh_crypto_free(hex);
}
diff --git a/src/bind.c b/src/bind.c
index b326a52b..c2917865 100644
--- a/src/bind.c
+++ b/src/bind.c
@@ -79,6 +79,7 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
int opt = 1;
socket_t s;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ZERO_STRUCT(hints);
@@ -98,7 +99,8 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
ai->ai_socktype,
ai->ai_protocol);
if (s == SSH_INVALID_SOCKET) {
- ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno));
+ ssh_set_error(sshbind, SSH_FATAL, "%s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
freeaddrinfo (ai);
return -1;
}
@@ -108,7 +110,7 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
ssh_set_error(sshbind,
SSH_FATAL,
"Setting socket options failed: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
freeaddrinfo (ai);
CLOSE_SOCKET(s);
return -1;
@@ -120,7 +122,7 @@ static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
"Binding to %s:%d: %s",
hostname,
port,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
freeaddrinfo (ai);
CLOSE_SOCKET(s);
return -1;
@@ -147,15 +149,6 @@ ssh_bind ssh_bind_new(void) {
static int ssh_bind_import_keys(ssh_bind sshbind) {
int rc;
- if (sshbind->ecdsakey == NULL &&
- sshbind->dsakey == NULL &&
- sshbind->rsakey == NULL &&
- sshbind->ed25519key == NULL) {
- ssh_set_error(sshbind, SSH_FATAL,
- "ECDSA, ED25519, DSA, or RSA host key file must be set");
- return SSH_ERROR;
- }
-
#ifdef HAVE_ECC
if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
@@ -179,30 +172,6 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
}
#endif
-#ifdef HAVE_DSA
- if (sshbind->dsa == NULL && sshbind->dsakey != NULL) {
- rc = ssh_pki_import_privkey_file(sshbind->dsakey,
- NULL,
- NULL,
- NULL,
- &sshbind->dsa);
- if (rc == SSH_ERROR || rc == SSH_EOF) {
- ssh_set_error(sshbind, SSH_FATAL,
- "Failed to import private DSA host key");
- return SSH_ERROR;
- }
-
- if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) {
- ssh_set_error(sshbind, SSH_FATAL,
- "The DSA host key has the wrong type: %d",
- ssh_key_type(sshbind->dsa));
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
- return SSH_ERROR;
- }
- }
-#endif
-
if (sshbind->rsa == NULL && sshbind->rsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->rsakey,
NULL,
@@ -249,97 +218,124 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
}
int ssh_bind_listen(ssh_bind sshbind) {
- const char *host;
- socket_t fd;
- int rc;
+ const char *host;
+ socket_t fd;
+ int rc;
- if (sshbind->rsa == NULL &&
- sshbind->dsa == NULL &&
- sshbind->ecdsa == NULL &&
- sshbind->ed25519 == NULL) {
- rc = ssh_bind_import_keys(sshbind);
- if (rc != SSH_OK) {
- return SSH_ERROR;
- }
- }
+ /* Apply global bind configurations, if it hasn't been applied before */
+ rc = ssh_bind_options_parse_config(sshbind, NULL);
+ if (rc != 0) {
+ ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
+ return SSH_ERROR;
+ }
- if (sshbind->bindfd == SSH_INVALID_SOCKET) {
- host = sshbind->bindaddr;
- if (host == NULL) {
- host = "0.0.0.0";
- }
+ /* Set default hostkey paths if no hostkey was found before */
+ if (sshbind->ecdsakey == NULL &&
+ sshbind->rsakey == NULL &&
+ sshbind->ed25519key == NULL) {
- fd = bind_socket(sshbind, host, sshbind->bindport);
- if (fd == SSH_INVALID_SOCKET) {
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
- ssh_key_free(sshbind->rsa);
- sshbind->rsa = NULL;
- /* XXX should this clear also other structures that were allocated */
- return -1;
- }
+ sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
+ sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
+ sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
+ }
- if (listen(fd, 10) < 0) {
- ssh_set_error(sshbind, SSH_FATAL,
- "Listening to socket %d: %s",
- fd, strerror(errno));
- CLOSE_SOCKET(fd);
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
- ssh_key_free(sshbind->rsa);
- sshbind->rsa = NULL;
- /* XXX should this clear also other structures that were allocated */
- return -1;
- }
+ /* Apply global bind configurations, if it hasn't been applied before */
+ rc = ssh_bind_options_parse_config(sshbind, NULL);
+ if (rc != 0) {
+ ssh_set_error(sshbind, SSH_FATAL, "Could not parse global config");
+ return SSH_ERROR;
+ }
+
+ /* Set default hostkey paths if no hostkey was found before */
+ if (sshbind->ecdsakey == NULL &&
+ sshbind->rsakey == NULL &&
+ sshbind->ed25519key == NULL) {
+
+ sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
+ sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
+ sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
+ }
+
+ if (sshbind->rsa == NULL &&
+ sshbind->ecdsa == NULL &&
+ sshbind->ed25519 == NULL) {
+ rc = ssh_bind_import_keys(sshbind);
+ if (rc != SSH_OK) {
+ return SSH_ERROR;
+ }
+ }
+
+ if (sshbind->bindfd == SSH_INVALID_SOCKET) {
+ host = sshbind->bindaddr;
+ if (host == NULL) {
+ host = "0.0.0.0";
+ }
+
+ fd = bind_socket(sshbind, host, sshbind->bindport);
+ if (fd == SSH_INVALID_SOCKET) {
+ return SSH_ERROR;
+ }
+
+ if (listen(fd, 10) < 0) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Listening to socket %d: %s",
+ fd,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
+ return SSH_ERROR;
+ }
- sshbind->bindfd = fd;
+ sshbind->bindfd = fd;
} else {
- SSH_LOG(SSH_LOG_INFO, "Using app-provided bind socket");
+ SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
}
return 0;
}
-int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks,
- void *userdata){
- if (sshbind == NULL) {
- return SSH_ERROR;
- }
- if (callbacks == NULL) {
- ssh_set_error_invalid(sshbind);
- return SSH_ERROR;
- }
- if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){
- ssh_set_error(sshbind,SSH_FATAL,
- "Invalid callback passed in (badly initialized)");
- return SSH_ERROR;
- }
- sshbind->bind_callbacks = callbacks;
- sshbind->bind_callbacks_userdata=userdata;
- return 0;
+int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, void *userdata)
+{
+ if (sshbind == NULL) {
+ return SSH_ERROR;
+ }
+ if (callbacks == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return SSH_ERROR;
+ }
+ if (callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)) {
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Invalid callback passed in (badly initialized)");
+ return SSH_ERROR;
+ }
+ sshbind->bind_callbacks = callbacks;
+ sshbind->bind_callbacks_userdata = userdata;
+ return 0;
}
/** @internal
* @brief callback being called by poll when an event happens
*
*/
-static int ssh_bind_poll_callback(ssh_poll_handle sshpoll,
- socket_t fd, int revents, void *user){
- ssh_bind sshbind=(ssh_bind)user;
- (void)sshpoll;
- (void)fd;
-
- if(revents & POLLIN){
- /* new incoming connection */
- if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){
- sshbind->bind_callbacks->incoming_connection(sshbind,
- sshbind->bind_callbacks_userdata);
+static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, socket_t fd, int revents, void *user)
+{
+ ssh_bind sshbind = (ssh_bind)user;
+ (void)sshpoll;
+ (void)fd;
+
+ if (revents & POLLIN) {
+ /* new incoming connection */
+ if (ssh_callbacks_exists(sshbind->bind_callbacks, incoming_connection)) {
+ sshbind->bind_callbacks->incoming_connection(sshbind,
+ sshbind->bind_callbacks_userdata);
+ }
}
- }
- return 0;
+ return 0;
}
/** @internal
- * @brief returns the current poll handle, or create it
+ * @brief returns the current poll handle, or creates it
* @param sshbind the ssh_bind object
* @returns a ssh_poll handle suitable for operation
*/
@@ -363,20 +359,24 @@ ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind)
return sshbind->poll;
}
-void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) {
- sshbind->blocking = blocking ? 1 : 0;
+void ssh_bind_set_blocking(ssh_bind sshbind, int blocking)
+{
+ sshbind->blocking = blocking ? 1 : 0;
}
-socket_t ssh_bind_get_fd(ssh_bind sshbind) {
- return sshbind->bindfd;
+socket_t ssh_bind_get_fd(ssh_bind sshbind)
+{
+ return sshbind->bindfd;
}
-void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) {
- sshbind->bindfd = fd;
+void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd)
+{
+ sshbind->bindfd = fd;
}
-void ssh_bind_fd_toaccept(ssh_bind sshbind) {
- sshbind->toaccept = 1;
+void ssh_bind_fd_toaccept(ssh_bind sshbind)
+{
+ sshbind->toaccept = 1;
}
void ssh_bind_free(ssh_bind sshbind){
@@ -393,17 +393,15 @@ void ssh_bind_free(ssh_bind sshbind){
/* options */
SAFE_FREE(sshbind->banner);
+ SAFE_FREE(sshbind->moduli_file);
SAFE_FREE(sshbind->bindaddr);
SAFE_FREE(sshbind->config_dir);
SAFE_FREE(sshbind->pubkey_accepted_key_types);
- SAFE_FREE(sshbind->dsakey);
SAFE_FREE(sshbind->rsakey);
SAFE_FREE(sshbind->ecdsakey);
SAFE_FREE(sshbind->ed25519key);
- ssh_key_free(sshbind->dsa);
- sshbind->dsa = NULL;
ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
ssh_key_free(sshbind->ecdsa);
@@ -420,7 +418,9 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind);
}
-int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
+int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
+{
+ ssh_poll_handle handle = NULL;
int i, rc;
if (sshbind == NULL) {
@@ -432,13 +432,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
return SSH_ERROR;
}
- /* Apply global bind configurations, if it hasn't been applied before */
- rc = ssh_bind_options_parse_config(sshbind, NULL);
- if (rc != 0) {
- ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
- return SSH_ERROR;
- }
-
session->server = 1;
/* Copy options from bind to session */
@@ -485,8 +478,25 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
}
session->common.log_verbosity = sshbind->common.log_verbosity;
- if(sshbind->banner != NULL)
- session->opts.custombanner = strdup(sshbind->banner);
+
+ if (sshbind->banner != NULL) {
+ session->opts.custombanner = strdup(sshbind->banner);
+ if (session->opts.custombanner == NULL) {
+ ssh_set_error_oom(sshbind);
+ return SSH_ERROR;
+ }
+ }
+
+ if (sshbind->moduli_file != NULL) {
+ session->opts.moduli_file = strdup(sshbind->moduli_file);
+ if (session->opts.moduli_file == NULL) {
+ ssh_set_error_oom(sshbind);
+ return SSH_ERROR;
+ }
+ }
+
+ session->opts.rsa_min_size = sshbind->rsa_min_size;
+
ssh_socket_free(session->socket);
session->socket = ssh_socket_new(session);
if (session->socket == NULL) {
@@ -495,7 +505,12 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
return SSH_ERROR;
}
ssh_socket_set_fd(session->socket, fd);
- ssh_socket_get_poll_handle(session->socket);
+ handle = ssh_socket_get_poll_handle(session->socket);
+ if (handle == NULL) {
+ ssh_set_error_oom(sshbind);
+ return SSH_ERROR;
+ }
+ ssh_socket_set_connected(session->socket, handle);
/* We must try to import any keys that could be imported in case
* we are not using ssh_bind_listen (which is the other place
@@ -503,7 +518,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
* only using ssh_bind_accept_fd to manage sockets ourselves.
*/
if (sshbind->rsa == NULL &&
- sshbind->dsa == NULL &&
sshbind->ecdsa == NULL &&
sshbind->ed25519 == NULL) {
rc = ssh_bind_import_keys(sshbind);
@@ -521,15 +535,6 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
}
}
#endif
-#ifdef HAVE_DSA
- if (sshbind->dsa) {
- session->srv.dsa_key = ssh_key_dup(sshbind->dsa);
- if (session->srv.dsa_key == NULL) {
- ssh_set_error_oom(sshbind);
- return SSH_ERROR;
- }
- }
-#endif
if (sshbind->rsa) {
session->srv.rsa_key = ssh_key_dup(sshbind->rsa);
if (session->srv.rsa_key == NULL) {
@@ -568,9 +573,16 @@ int ssh_bind_accept(ssh_bind sshbind, ssh_session session)
fd = accept(sshbind->bindfd, NULL, NULL);
if (fd == SSH_INVALID_SOCKET) {
- ssh_set_error(sshbind, SSH_FATAL,
- "Accepting a new connection: %s",
- strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ if (errno == EINTR) {
+ ssh_set_error(sshbind, SSH_EINTR,
+ "Accepting a new connection (child signal error): %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ } else {
+ ssh_set_error(sshbind, SSH_FATAL,
+ "Accepting a new connection: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ }
return SSH_ERROR;
}
rc = ssh_bind_accept_fd(sshbind, session, fd);
diff --git a/src/bind_config.c b/src/bind_config.c
index 14b84db0..a4c7a8d7 100644
--- a/src/bind_config.c
+++ b/src/bind_config.c
@@ -40,7 +40,9 @@
#include "libssh/server.h"
#include "libssh/options.h"
+#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 1024
+#endif
/* Flags used for the parser state */
#define PARSING 1
@@ -187,18 +189,29 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *line,
unsigned int count,
uint32_t *parser_flags,
- uint8_t *seen);
-
-static void local_parse_file(ssh_bind bind,
- const char *filename,
- uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth);
+
+#define LIBSSH_BIND_CONF_MAX_DEPTH 16
+static void
+local_parse_file(ssh_bind bind,
+ const char *filename,
+ uint32_t *parser_flags,
+ uint8_t *seen,
+ unsigned int depth)
{
FILE *f;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
+ if (depth > LIBSSH_BIND_CONF_MAX_DEPTH) {
+ ssh_set_error(bind, SSH_FATAL,
+ "ERROR - Too many levels of configuration includes "
+ "when processing file '%s'", filename);
+ return;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
@@ -211,7 +224,7 @@ static void local_parse_file(ssh_bind bind,
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen);
+ rv = ssh_bind_config_parse_line(bind, line, count, parser_flags, seen, depth);
if (rv < 0) {
fclose(f);
return;
@@ -226,7 +239,8 @@ static void local_parse_file(ssh_bind bind,
static void local_parse_glob(ssh_bind bind,
const char *fileglob,
uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth)
{
glob_t globbuf = {
.gl_flags = 0,
@@ -246,7 +260,7 @@ static void local_parse_glob(ssh_bind bind,
}
for (i = 0; i < globbuf.gl_pathc; i++) {
- local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen);
+ local_parse_file(bind, globbuf.gl_pathv[i], parser_flags, seen, depth);
}
globfree(&globbuf);
@@ -272,7 +286,8 @@ ssh_bind_config_parse_line(ssh_bind bind,
const char *line,
unsigned int count,
uint32_t *parser_flags,
- uint8_t *seen)
+ uint8_t *seen,
+ unsigned int depth)
{
enum ssh_bind_config_opcode_e opcode;
const char *p = NULL;
@@ -286,7 +301,12 @@ ssh_bind_config_parse_line(ssh_bind bind,
return -1;
}
- if ((line == NULL) || (parser_flags == NULL)) {
+ /* Ignore empty lines */
+ if (line == NULL || *line == '\0') {
+ return 0;
+ }
+
+ if (parser_flags == NULL) {
ssh_set_error_invalid(bind);
return -1;
}
@@ -331,9 +351,9 @@ ssh_bind_config_parse_line(ssh_bind bind,
p = ssh_config_get_str_tok(&s, NULL);
if (p && (*parser_flags & PARSING)) {
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
- local_parse_glob(bind, p, parser_flags, seen);
+ local_parse_glob(bind, p, parser_flags, seen, depth + 1);
#else
- local_parse_file(bind, p, parser_flags, seen);
+ local_parse_file(bind, p, parser_flags, seen, depth + 1);
#endif /* HAVE_GLOB */
}
break;
@@ -343,7 +363,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HOSTKEY, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set Hostkey value '%s'",
count, p);
}
@@ -354,7 +374,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set ListenAddress value '%s'",
count, p);
}
@@ -365,7 +385,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT_STR, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set Port value '%s'",
count, p);
}
@@ -376,7 +396,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_C_S, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set C->S Ciphers value '%s'",
count, p);
break;
@@ -384,7 +404,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_CIPHERS_S_C, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set S->C Ciphers value '%s'",
count, p);
}
@@ -395,7 +415,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_C_S, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set C->S MAC value '%s'",
count, p);
break;
@@ -403,7 +423,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_HMAC_S_C, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set S->C MAC value '%s'",
count, p);
}
@@ -417,10 +437,10 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (strcasecmp(p, "quiet") == 0) {
value = SSH_LOG_NONE;
} else if (strcasecmp(p, "fatal") == 0 ||
- strcasecmp(p, "error")== 0 ||
- strcasecmp(p, "info") == 0) {
+ strcasecmp(p, "error")== 0) {
value = SSH_LOG_WARN;
- } else if (strcasecmp(p, "verbose") == 0) {
+ } else if (strcasecmp(p, "verbose") == 0 ||
+ strcasecmp(p, "info") == 0) {
value = SSH_LOG_INFO;
} else if (strcasecmp(p, "DEBUG") == 0 ||
strcasecmp(p, "DEBUG1") == 0) {
@@ -433,7 +453,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY,
&value);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set LogLevel value '%s'",
count, p);
}
@@ -445,7 +465,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
if (p && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_KEY_EXCHANGE, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set KexAlgorithms value '%s'",
count, p);
}
@@ -520,13 +540,13 @@ ssh_bind_config_parse_line(ssh_bind bind,
/* Skip one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
- SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument\n", count, p2);
SAFE_FREE(x);
return -1;
}
args++;
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_DEBUG,
"line %d: Unsupported Match keyword '%s', ignoring\n",
count,
p2);
@@ -556,7 +576,7 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind,
SSH_BIND_OPTIONS_PUBKEY_ACCEPTED_KEY_TYPES, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set PubKeyAcceptedKeyTypes value '%s'",
count, p);
}
@@ -568,26 +588,26 @@ ssh_bind_config_parse_line(ssh_bind bind,
rc = ssh_bind_options_set(bind,
SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, p);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set HostkeyAlgorithms value '%s'",
count, p);
}
}
break;
case BIND_CFG_NOT_ALLOWED_IN_MATCH:
- SSH_LOG(SSH_LOG_WARN, "Option not allowed in Match block: %s, line: %d",
+ SSH_LOG(SSH_LOG_DEBUG, "Option not allowed in Match block: %s, line: %d",
keyword, count);
break;
case BIND_CFG_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
keyword, count);
break;
case BIND_CFG_UNSUPPORTED:
- SSH_LOG(SSH_LOG_WARN, "Unsupported option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported option: %s, line: %d",
keyword, count);
break;
case BIND_CFG_NA:
- SSH_LOG(SSH_LOG_WARN, "Option not applicable: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Option not applicable: %s, line: %d",
keyword, count);
break;
default:
@@ -626,7 +646,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
parser_flags = PARSING;
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen);
+ rv = ssh_bind_config_parse_line(bind, line, count, &parser_flags, seen, 0);
if (rv) {
fclose(f);
return -1;
@@ -636,3 +656,64 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
fclose(f);
return 0;
}
+
+/* @brief Parse configuration string and set the options to the given bind session
+ *
+ * @params[in] bind The ssh bind session
+ * @params[in] input Null terminated string containing the configuration
+ *
+ * @returns SSH_OK on successful parsing the configuration string,
+ * SSH_ERROR on error
+ */
+int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
+{
+ char line[MAX_LINE_SIZE] = {0};
+ const char *c = input, *line_start = input;
+ unsigned int line_num = 0, line_len;
+ uint32_t parser_flags;
+ int rv;
+
+ /* This local table is used during the parsing of the current file (and
+ * files included recursively in this file) to prevent an option to be
+ * redefined, i.e. the first value set is kept. But this DO NOT prevent the
+ * option to be redefined later by another file. */
+ uint8_t seen[BIND_CFG_MAX] = {0};
+
+ SSH_LOG(SSH_LOG_DEBUG, "Reading bind configuration data from string:");
+ SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input);
+
+ parser_flags = PARSING;
+ while (1) {
+ line_num++;
+ line_start = c;
+ c = strchr(line_start, '\n');
+ if (c == NULL) {
+ /* if there is no newline at the end of the string */
+ c = strchr(line_start, '\0');
+ }
+ if (c == NULL) {
+ /* should not happen, would mean a string without trailing '\0' */
+ SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string");
+ return SSH_ERROR;
+ }
+ line_len = c - line_start;
+ if (line_len > MAX_LINE_SIZE - 1) {
+ SSH_LOG(SSH_LOG_WARN, "Line %u too long: %u characters",
+ line_num, line_len);
+ return SSH_ERROR;
+ }
+ memcpy(line, line_start, line_len);
+ line[line_len] = '\0';
+ SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
+ rv = ssh_bind_config_parse_line(bind, line, line_num, &parser_flags, seen, 0);
+ if (rv < 0) {
+ return SSH_ERROR;
+ }
+ if (*c == '\0') {
+ break;
+ }
+ c++;
+ }
+
+ return SSH_OK;
+}
diff --git a/src/buffer.c b/src/buffer.c
index ce12f491..299eac54 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -26,6 +26,7 @@
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <stdio.h>
#ifndef _WIN32
#include <netinet/in.h>
@@ -46,9 +47,9 @@
*/
struct ssh_buffer_struct {
bool secure;
- size_t used;
- size_t allocated;
- size_t pos;
+ uint32_t used;
+ uint32_t allocated;
+ uint32_t pos;
uint8_t *data;
};
@@ -56,7 +57,7 @@ struct ssh_buffer_struct {
#define BUFFER_SIZE_MAX 0x10000000
/**
- * @defgroup libssh_buffer The SSH buffer functions.
+ * @defgroup libssh_buffer The SSH buffer functions
* @ingroup libssh
*
* Functions to handle SSH buffers.
@@ -83,21 +84,21 @@ static void buffer_verify(ssh_buffer buf)
if (buf->used > buf->allocated) {
fprintf(stderr,
- "BUFFER ERROR: allocated %zu, used %zu\n",
+ "BUFFER ERROR: allocated %u, used %u\n",
buf->allocated,
buf->used);
do_abort = true;
}
if (buf->pos > buf->used) {
fprintf(stderr,
- "BUFFER ERROR: position %zu, used %zu\n",
+ "BUFFER ERROR: position %u, used %u\n",
buf->pos,
buf->used);
do_abort = true;
}
if (buf->pos > buf->allocated) {
fprintf(stderr,
- "BUFFER ERROR: position %zu, allocated %zu\n",
+ "BUFFER ERROR: position %u, allocated %u\n",
buf->pos,
buf->allocated);
do_abort = true;
@@ -129,7 +130,7 @@ struct ssh_buffer_struct *ssh_buffer_new(void)
/*
* Always preallocate 64 bytes.
*
- * -1 for ralloc_buffer magic.
+ * -1 for realloc_buffer magic.
*/
rc = ssh_buffer_allocate_size(buf, 64 - 1);
if (rc != 0) {
@@ -178,9 +179,9 @@ void ssh_buffer_set_secure(ssh_buffer buffer)
buffer->secure = true;
}
-static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed)
+static int realloc_buffer(struct ssh_buffer_struct *buffer, uint32_t needed)
{
- size_t smallest = 1;
+ uint32_t smallest = 1;
uint8_t *new = NULL;
buffer_verify(buffer);
@@ -693,7 +694,7 @@ uint32_t ssh_buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint3
/**
* @internal
*
- * @brief Get a 8 bits unsigned int out of the buffer and adjusts the read
+ * @brief Get a 8 bits unsigned int out of the buffer and adjust the read
* pointer.
*
* @param[in] buffer The buffer to read.
@@ -702,7 +703,7 @@ uint32_t ssh_buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint3
*
* @returns 0 if there is not enough data in buffer, 1 otherwise.
*/
-int ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
+uint32_t ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
return ssh_buffer_get_data(buffer,data,sizeof(uint8_t));
}
@@ -717,7 +718,7 @@ int ssh_buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
*
* @returns 0 if there is not enough data in buffer, 4 otherwise.
*/
-int ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
+uint32_t ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
return ssh_buffer_get_data(buffer,data,sizeof(uint32_t));
}
/**
@@ -732,12 +733,12 @@ int ssh_buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
*
* @returns 0 if there is not enough data in buffer, 8 otherwise.
*/
-int ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
+uint32_t ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
return ssh_buffer_get_data(buffer,data,sizeof(uint64_t));
}
/**
- * @brief Valdiates that the given length can be obtained from the buffer.
+ * @brief Validates that the given length can be obtained from the buffer.
*
* @param[in] buffer The buffer to read from.
*
@@ -747,7 +748,8 @@ int ssh_buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
*/
int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len)
{
- if (buffer->pos + len < len || buffer->pos + len > buffer->used) {
+ if (buffer == NULL || buffer->pos + len < len ||
+ buffer->pos + len > buffer->used) {
return SSH_ERROR;
}
@@ -757,7 +759,7 @@ int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len)
/**
* @internal
*
- * @brief Get a SSH String out of the buffer and adjusts the read pointer.
+ * @brief Get an SSH String out of the buffer and adjust the read pointer.
*
* @param[in] buffer The buffer to read.
*
@@ -868,7 +870,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
va_arg(ap, bignum);
/*
* Use a fixed size for a bignum
- * (they should normaly be around 32)
+ * (they should normally be around 32)
*/
needed_size += 64;
break;
@@ -878,7 +880,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
cstring = NULL;
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
rc = SSH_ERROR;
}
if (rc != SSH_OK){
@@ -900,7 +902,7 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
}
}
- rc = ssh_buffer_allocate_size(buffer, needed_size);
+ rc = ssh_buffer_allocate_size(buffer, (uint32_t)needed_size);
if (rc != 0) {
return SSH_ERROR;
}
@@ -1007,7 +1009,7 @@ int ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
cstring = NULL;
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
rc = SSH_ERROR;
}
if (rc != SSH_OK){
@@ -1102,7 +1104,8 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
bignum *bignum;
void **data;
} o;
- size_t len, rlen, max_len;
+ size_t len;
+ uint32_t rlen, max_len;
ssh_string tmp_string = NULL;
va_list ap_copy;
size_t count;
@@ -1238,7 +1241,7 @@ int ssh_buffer_unpack_va(struct ssh_buffer_struct *buffer,
rc = SSH_OK;
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid buffer format %c", *p);
}
if (rc != SSH_OK) {
break;
diff --git a/src/chachapoly.c b/src/chachapoly.c
index c90a1e97..2cd23854 100644
--- a/src/chachapoly.c
+++ b/src/chachapoly.c
@@ -65,7 +65,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
/**
* @internal
*
- * @brief encrypts an outgoing packet with chacha20 and authenticate it
+ * @brief encrypts an outgoing packet with chacha20 and authenticates it
* with poly1305.
*/
static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
@@ -164,7 +164,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
ssh_log_hexdump("received tag", mac, POLY1305_TAGLEN);
#endif
- cmp = memcmp(tag, mac, POLY1305_TAGLEN);
+ cmp = secure_memcmp(tag, mac, POLY1305_TAGLEN);
if(cmp != 0) {
/* mac error */
SSH_LOG(SSH_LOG_PACKET,"poly1305 verify error");
diff --git a/src/channels.c b/src/channels.c
index 607bd568..9e613715 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -29,6 +29,10 @@
#include <errno.h>
#include <time.h>
#include <stdbool.h>
+#include <string.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
#ifndef _WIN32
#include <netinet/in.h>
@@ -48,16 +52,19 @@
#include "libssh/server.h"
#endif
-#define WINDOWBASE 1280000
-#define WINDOWLIMIT (WINDOWBASE/2)
-
/*
* All implementations MUST be able to process packets with an
* uncompressed payload length of 32768 bytes or less and a total packet
* size of 35000 bytes or less.
*/
#define CHANNEL_MAX_PACKET 32768
-#define CHANNEL_INITIAL_WINDOW 64000
+
+/*
+ * WINDOW_DEFAULT matches the default OpenSSH session window size.
+ * This controls how much data the peer can send before needing to receive
+ * a round-trip SSH2_MSG_CHANNEL_WINDOW_ADJUST message that increases the window.
+ */
+#define WINDOW_DEFAULT (64*CHANNEL_MAX_PACKET)
/**
* @defgroup libssh_channel The SSH channel functions
@@ -76,6 +83,9 @@ static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet);
* @param[in] session The ssh session to use.
*
* @return A pointer to a newly allocated channel, NULL on error.
+ * The channel needs to be freed with ssh_channel_free().
+ *
+ * @see ssh_channel_free()
*/
ssh_channel ssh_channel_new(ssh_session session)
{
@@ -117,6 +127,13 @@ ssh_channel ssh_channel_new(ssh_session session)
if (session->channels == NULL) {
session->channels = ssh_list_new();
+ if (session->channels == NULL) {
+ ssh_set_error_oom(session);
+ SSH_BUFFER_FREE(channel->stdout_buffer);
+ SSH_BUFFER_FREE(channel->stderr_buffer);
+ SAFE_FREE(channel);
+ return NULL;
+ }
}
ssh_list_prepend(session->channels, channel);
@@ -137,8 +154,9 @@ ssh_channel ssh_channel_new(ssh_session session)
*
* @return The new channel identifier.
*/
-uint32_t ssh_channel_new_id(ssh_session session) {
- return ++(session->maxchannel);
+uint32_t ssh_channel_new_id(ssh_session session)
+{
+ return ++(session->maxchannel);
}
/**
@@ -163,7 +181,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
channel=ssh_channel_from_local(session,channelid);
if(channel==NULL){
ssh_set_error(session, SSH_FATAL,
- "Unknown channel id %"PRIu32,
+ "Unknown channel id %" PRIu32,
(uint32_t) channelid);
/* TODO: Set error marking in channel object */
@@ -177,8 +195,8 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
if (rc != SSH_OK)
goto error;
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Received a CHANNEL_OPEN_CONFIRMATION for channel %" PRIu32 ":%" PRIu32,
channel->local_channel,
channel->remote_channel);
@@ -190,13 +208,21 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Remote window : %"PRIu32", maxpacket : %"PRIu32,
- (uint32_t) channel->remote_window,
- (uint32_t) channel->remote_maxpacket);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Remote window : %" PRIu32 ", maxpacket : %" PRIu32,
+ channel->remote_window,
+ channel->remote_maxpacket);
channel->state = SSH_CHANNEL_STATE_OPEN;
channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_open_response_function,
+ channel->session,
+ channel,
+ true /* is_success */);
+
return SSH_PACKET_USED;
error:
@@ -235,16 +261,25 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_fail){
"SSH2_MSG_CHANNEL_OPEN_FAILURE received in incorrect channel "
"state %d",
channel->state);
+ SAFE_FREE(error);
goto error;
}
ssh_set_error(session, SSH_REQUEST_DENIED,
- "Channel opening failure: channel %u error (%"PRIu32") %s",
+ "Channel opening failure: channel %" PRIu32 " error (%" PRIu32 ") %s",
channel->local_channel,
- (uint32_t) code,
+ code,
error);
SAFE_FREE(error);
channel->state=SSH_CHANNEL_STATE_OPEN_DENIED;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_open_response_function,
+ channel->session,
+ channel,
+ false /* is_success */);
+
return SSH_PACKET_USED;
error:
@@ -252,7 +287,8 @@ error:
return SSH_PACKET_USED;
}
-static int ssh_channel_open_termination(void *c){
+static int ssh_channel_open_termination(void *c)
+{
ssh_channel channel = (ssh_channel) c;
if (channel->state != SSH_CHANNEL_STATE_OPENING ||
channel->session->session_state == SSH_SESSION_STATE_ERROR)
@@ -310,8 +346,8 @@ channel_open(ssh_channel channel,
channel->local_maxpacket = maxpacket;
channel->local_window = window;
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Creating a channel %d with %d window and %d max packet",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Creating a channel %" PRIu32 " with %" PRIu32 " window and %" PRIu32 " max packet",
channel->local_channel, window, maxpacket);
rc = ssh_buffer_pack(session->out_buffer,
@@ -339,7 +375,7 @@ channel_open(ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d",
+ "Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %" PRIu32,
type, channel->local_channel);
pending:
@@ -359,7 +395,7 @@ end:
if (channel->state == SSH_CHANNEL_STATE_OPEN) {
err = SSH_OK;
} else if (err != SSH_AGAIN) {
- /* Messages were handled correctly, but he channel state is invalid */
+ /* Messages were handled correctly, but the channel state is invalid */
err = SSH_ERROR;
}
@@ -386,35 +422,46 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id) {
/**
* @internal
- * @brief grows the local window and send a packet to the other party
+ * @brief grows the local window and sends a packet to the other party
* @param session SSH session
* @param channel SSH channel
- * @param minimumsize The minimum acceptable size for the new window.
* @return SSH_OK if successful; SSH_ERROR otherwise.
*/
static int grow_window(ssh_session session,
- ssh_channel channel,
- uint32_t minimumsize)
+ ssh_channel channel)
{
- uint32_t new_window = minimumsize > WINDOWBASE ? minimumsize : WINDOWBASE;
+ uint32_t used;
+ uint32_t increment;
int rc;
- if(new_window <= channel->local_window){
- SSH_LOG(SSH_LOG_PROTOCOL,
- "growing window (channel %d:%d) to %d bytes : not needed (%d bytes)",
- channel->local_channel, channel->remote_channel, new_window,
+ /* Calculate the increment taking into account what the peer may still send
+ * (local_window) and what we've already buffered (stdout_buffer and
+ * stderr_buffer).
+ */
+ used = channel->local_window;
+ if (channel->stdout_buffer != NULL) {
+ used += ssh_buffer_get_len(channel->stdout_buffer);
+ }
+ if (channel->stderr_buffer != NULL) {
+ used += ssh_buffer_get_len(channel->stderr_buffer);
+ }
+ /* Avoid a negative increment in case the peer sent more than the window allowed */
+ increment = WINDOW_DEFAULT > used ? WINDOW_DEFAULT - used : 0;
+ /* Don't grow until we can request at least half a window */
+ if (increment < (WINDOW_DEFAULT / 2)) {
+ SSH_LOG(SSH_LOG_DEBUG,
+ "growing window (channel %" PRIu32 ":%" PRIu32 ") to %" PRIu32 " bytes : not needed (%" PRIu32 " bytes)",
+ channel->local_channel, channel->remote_channel, WINDOW_DEFAULT,
channel->local_window);
return SSH_OK;
}
- /* WINDOW_ADJUST packet needs a relative increment rather than an absolute
- * value, so we give here the missing bytes needed to reach new_window
- */
+
rc = ssh_buffer_pack(session->out_buffer,
"bdd",
SSH2_MSG_CHANNEL_WINDOW_ADJUST,
channel->remote_channel,
- new_window - channel->local_window);
+ increment);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
goto error;
@@ -424,13 +471,13 @@ static int grow_window(ssh_session session,
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "growing window (channel %d:%d) to %d bytes",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "growing window (channel %" PRIu32 ":%" PRIu32 ") by %" PRIu32 " bytes",
channel->local_channel,
channel->remote_channel,
- new_window);
+ increment);
- channel->local_window = new_window;
+ channel->local_window += increment;
return SSH_OK;
error:
@@ -452,7 +499,8 @@ error:
* @return The related ssh_channel, or NULL if the channel is
* unknown or the packet is invalid.
*/
-static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
+static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet)
+{
ssh_channel channel;
uint32_t chan;
int rc;
@@ -467,7 +515,7 @@ static ssh_channel channel_from_msg(ssh_session session, ssh_buffer packet) {
channel = ssh_channel_from_local(session, chan);
if (channel == NULL) {
ssh_set_error(session, SSH_FATAL,
- "Server specified invalid channel %"PRIu32,
+ "Server specified invalid channel %" PRIu32,
(uint32_t) chan);
}
@@ -478,6 +526,8 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) {
ssh_channel channel;
uint32_t bytes;
int rc;
+ bool was_empty;
+
(void)user;
(void)type;
@@ -494,123 +544,153 @@ SSH_PACKET_CALLBACK(channel_rcv_change_window) {
return SSH_PACKET_USED;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Adding %d bytes to channel (%d:%d) (from %d bytes)",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Adding %" PRIu32 " bytes to channel (%" PRIu32 ":%" PRIu32 ") (from %" PRIu32 " bytes)",
bytes,
channel->local_channel,
channel->remote_channel,
channel->remote_window);
+ was_empty = channel->remote_window == 0;
+
channel->remote_window += bytes;
+ /* Writing to the channel is non-blocking until the receive window is empty.
+ When the receive window becomes non-zero again, call channel_write_wontblock_function. */
+ if (was_empty && bytes > 0) {
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_write_wontblock_function,
+ session,
+ channel,
+ channel->remote_window);
+ }
+
return SSH_PACKET_USED;
}
/* is_stderr is set to 1 if the data are extended, ie stderr */
-SSH_PACKET_CALLBACK(channel_rcv_data){
- ssh_channel channel;
- ssh_string str;
- ssh_buffer buf;
- size_t len;
- int is_stderr;
- int rest;
- (void)user;
+SSH_PACKET_CALLBACK(channel_rcv_data)
+{
+ ssh_channel channel = NULL;
+ ssh_string str = NULL;
+ ssh_buffer buf = NULL;
+ void *data = NULL;
+ uint32_t len;
+ int extended, is_stderr = 0;
+ int rest;
- if(type==SSH2_MSG_CHANNEL_DATA)
- is_stderr=0;
- else
- is_stderr=1;
+ (void)user;
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS,
- "%s", ssh_get_error(session));
+ if (type == SSH2_MSG_CHANNEL_DATA) {
+ extended = 0;
+ } else { /* SSH_MSG_CHANNEL_EXTENDED_DATA */
+ extended = 1;
+ }
- return SSH_PACKET_USED;
- }
+ channel = channel_from_msg(session, packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- if (is_stderr) {
- uint32_t ignore;
- /* uint32 data type code. we can ignore it */
- ssh_buffer_get_u32(packet, &ignore);
- }
+ return SSH_PACKET_USED;
+ }
- str = ssh_buffer_get_ssh_string(packet);
- if (str == NULL) {
- SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!");
+ if (extended) {
+ uint32_t data_type_code, rc;
+ rc = ssh_buffer_get_u32(packet, &data_type_code);
+ if (rc != sizeof(uint32_t)) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Failed to read data type code: rc = %" PRIu32, rc);
- return SSH_PACKET_USED;
- }
- len = ssh_string_len(str);
+ return SSH_PACKET_USED;
+ }
+ is_stderr = 1;
+ data_type_code = ntohl(data_type_code);
+ if (data_type_code != SSH2_EXTENDED_DATA_STDERR) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid data type code %" PRIu32 "!",
+ data_type_code);
+ }
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Channel receiving %zu bytes data in %d (local win=%d remote win=%d)",
- len,
- is_stderr,
- channel->local_window,
- channel->remote_window);
+ str = ssh_buffer_get_ssh_string(packet);
+ if (str == NULL) {
+ SSH_LOG(SSH_LOG_PACKET, "Invalid data packet!");
- /* What shall we do in this case? Let's accept it anyway */
- if (len > channel->local_window) {
- SSH_LOG(SSH_LOG_RARE,
- "Data packet too big for our window(%zu vs %d)",
- len,
- channel->local_window);
- }
+ return SSH_PACKET_USED;
+ }
+ len = ssh_string_len(str);
- if (channel_default_bufferize(channel, ssh_string_data(str), len,
- is_stderr) < 0) {
- SSH_STRING_FREE(str);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Channel receiving %" PRIu32 " bytes data%s (local win=%" PRIu32
+ " remote win=%" PRIu32 ")",
+ len,
+ is_stderr ? " in stderr" : "",
+ channel->local_window,
+ channel->remote_window);
- return SSH_PACKET_USED;
- }
+ if (len > channel->local_window) {
+ SSH_LOG(SSH_LOG_RARE,
+ "Data packet too big for our window(%" PRIu32 " vs %" PRIu32 ")",
+ len,
+ channel->local_window);
+
+ SSH_STRING_FREE(str);
+
+ ssh_set_error(session, SSH_FATAL, "Window exceeded");
+
+ return SSH_PACKET_USED;
+ }
+
+ data = ssh_string_data(str);
+ if (channel_default_bufferize(channel, data, len, is_stderr) < 0) {
+ SSH_STRING_FREE(str);
+
+ return SSH_PACKET_USED;
+ }
- if (len <= channel->local_window) {
channel->local_window -= len;
- } else {
- channel->local_window = 0; /* buggy remote */
- }
- SSH_LOG(SSH_LOG_PACKET,
- "Channel windows are now (local win=%d remote win=%d)",
- channel->local_window,
- channel->remote_window);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Channel windows are now (local win=%" PRIu32 " remote win=%" PRIu32 ")",
+ channel->local_window,
+ channel->remote_window);
- SSH_STRING_FREE(str);
+ SSH_STRING_FREE(str);
- if (is_stderr) {
- buf = channel->stderr_buffer;
- } else {
- buf = channel->stdout_buffer;
- }
+ if (is_stderr) {
+ buf = channel->stderr_buffer;
+ } else {
+ buf = channel->stdout_buffer;
+ }
- ssh_callbacks_iterate(channel->callbacks,
- ssh_channel_callbacks,
- channel_data_function) {
- if (ssh_buffer_get(buf) == NULL) {
- break;
- }
- rest = ssh_callbacks_iterate_exec(channel_data_function,
- channel->session,
- channel,
- ssh_buffer_get(buf),
- ssh_buffer_get_len(buf),
- is_stderr);
- if (rest > 0) {
- if (channel->counter != NULL) {
- channel->counter->in_bytes += rest;
- }
- ssh_buffer_pass_bytes(buf, rest);
- }
- }
- ssh_callbacks_iterate_end();
+ ssh_callbacks_iterate(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_data_function) {
+ if (ssh_buffer_get(buf) == NULL) {
+ break;
+ }
+ rest = ssh_callbacks_iterate_exec(channel_data_function,
+ channel->session,
+ channel,
+ ssh_buffer_get(buf),
+ ssh_buffer_get_len(buf),
+ is_stderr);
+ if (rest > 0) {
+ int rc;
+ if (channel->counter != NULL) {
+ channel->counter->in_bytes += rest;
+ }
+ ssh_buffer_pass_bytes(buf, rest);
- if (channel->local_window + ssh_buffer_get_len(buf) < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
- return -1;
- }
- }
- return SSH_PACKET_USED;
+ rc = grow_window(session, channel);
+ if (rc == SSH_ERROR) {
+ return -1;
+ }
+ }
+ }
+ ssh_callbacks_iterate_end();
+
+ return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(channel_rcv_eof) {
@@ -626,7 +706,7 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) {
}
SSH_LOG(SSH_LOG_PACKET,
- "Received eof on channel (%d:%d)",
+ "Received eof on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
/* channel->remote_window = 0; */
@@ -641,40 +721,55 @@ SSH_PACKET_CALLBACK(channel_rcv_eof) {
return SSH_PACKET_USED;
}
+static bool ssh_channel_has_unread_data(ssh_channel channel)
+{
+ if (channel == NULL) {
+ return false;
+ }
+
+ if ((channel->stdout_buffer &&
+ ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
+ (channel->stderr_buffer &&
+ ssh_buffer_get_len(channel->stderr_buffer) > 0))
+ {
+ return true;
+ }
+
+ return false;
+}
+
SSH_PACKET_CALLBACK(channel_rcv_close) {
- ssh_channel channel;
- (void)user;
- (void)type;
+ ssh_channel channel;
+ (void)user;
+ (void)type;
- channel = channel_from_msg(session,packet);
- if (channel == NULL) {
- SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
+ channel = channel_from_msg(session,packet);
+ if (channel == NULL) {
+ SSH_LOG(SSH_LOG_FUNCTIONS, "%s", ssh_get_error(session));
- return SSH_PACKET_USED;
- }
+ return SSH_PACKET_USED;
+ }
- SSH_LOG(SSH_LOG_PACKET,
- "Received close on channel (%d:%d)",
- channel->local_channel,
- channel->remote_channel);
-
- if ((channel->stdout_buffer &&
- ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
- (channel->stderr_buffer &&
- ssh_buffer_get_len(channel->stderr_buffer) > 0)) {
- channel->delayed_close = 1;
- } else {
- channel->state = SSH_CHANNEL_STATE_CLOSED;
- }
- if (channel->remote_eof == 0) {
- SSH_LOG(SSH_LOG_PACKET,
- "Remote host not polite enough to send an eof before close");
- }
- channel->remote_eof = 1;
- /*
- * The remote eof doesn't break things if there was still data into read
- * buffer because the eof is ignored until the buffer is empty.
- */
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received close on channel (%" PRIu32 ":%" PRIu32 ")",
+ channel->local_channel,
+ channel->remote_channel);
+
+ if (!ssh_channel_has_unread_data(channel)) {
+ channel->state = SSH_CHANNEL_STATE_CLOSED;
+ } else {
+ channel->delayed_close = 1;
+ }
+
+ if (channel->remote_eof == 0) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Remote host not polite enough to send an eof before close");
+ }
+ /*
+ * The remote eof doesn't break things if there was still data into read
+ * buffer because the eof is ignored until the buffer is empty.
+ */
+ channel->remote_eof = 1;
ssh_callbacks_execute_list(channel->callbacks,
ssh_channel_callbacks,
@@ -682,17 +777,17 @@ SSH_PACKET_CALLBACK(channel_rcv_close) {
channel->session,
channel);
- channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
- if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
- ssh_channel_do_free(channel);
+ channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE;
+ if(channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)
+ ssh_channel_do_free(channel);
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(channel_rcv_request) {
ssh_channel channel;
char *request=NULL;
- uint8_t status;
+ uint8_t want_reply;
int rc;
(void)user;
(void)type;
@@ -705,7 +800,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
rc = ssh_buffer_unpack(packet, "sb",
&request,
- &status);
+ &want_reply);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_PACKET, "Invalid MSG_CHANNEL_REQUEST");
return SSH_PACKET_USED;
@@ -798,7 +893,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
}
if(strcmp(request,"keepalive@openssh.com")==0){
SAFE_FREE(request);
- SSH_LOG(SSH_LOG_PROTOCOL,"Responding to Openssh's keepalive");
+ SSH_LOG(SSH_LOG_DEBUG,"Responding to Openssh's keepalive");
rc = ssh_buffer_pack(session->out_buffer,
"bd",
@@ -813,13 +908,34 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
}
if (strcmp(request, "auth-agent-req@openssh.com") == 0) {
+ int status;
+
SAFE_FREE(request);
- SSH_LOG(SSH_LOG_PROTOCOL, "Received an auth-agent-req request");
- ssh_callbacks_execute_list(channel->callbacks,
- ssh_channel_callbacks,
- channel_auth_agent_req_function,
- channel->session,
- channel);
+ SSH_LOG(SSH_LOG_DEBUG, "Received an auth-agent-req request");
+
+ status = SSH2_MSG_CHANNEL_FAILURE;
+ ssh_callbacks_iterate(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_auth_agent_req_function) {
+ ssh_callbacks_iterate_exec(channel_auth_agent_req_function,
+ channel->session,
+ channel);
+ /* in lieu of a return value, if the callback exists it's supported */
+ status = SSH2_MSG_CHANNEL_SUCCESS;
+ break;
+ }
+ ssh_callbacks_iterate_end();
+
+ if (want_reply) {
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bd",
+ status,
+ channel->remote_channel);
+ if (rc != SSH_OK) {
+ return SSH_PACKET_USED;
+ }
+ ssh_packet_send(session);
+ }
return SSH_PACKET_USED;
}
@@ -828,11 +944,11 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
* client requests. That means we need to create a ssh message to be passed
* to the user code handling ssh messages
*/
- ssh_message_handle_channel_request(session,channel,packet,request,status);
+ ssh_message_handle_channel_request(session,channel,packet,request,want_reply);
#else
- SSH_LOG(SSH_LOG_WARNING, "Unhandled channel request %s", request);
+ SSH_LOG(SSH_LOG_DEBUG, "Unhandled channel request %s", request);
#endif
-
+
SAFE_FREE(request);
return SSH_PACKET_USED;
@@ -845,7 +961,7 @@ SSH_PACKET_CALLBACK(channel_rcv_request) {
* FIXME is the window changed?
*/
int channel_default_bufferize(ssh_channel channel,
- void *data, size_t len,
+ void *data, uint32_t len,
bool is_stderr)
{
ssh_session session;
@@ -862,7 +978,7 @@ int channel_default_bufferize(ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "placing %zu bytes into channel buffer (%s)",
+ "placing %" PRIu32 " bytes into channel buffer (%s)",
len,
is_stderr ? "stderr" : "stdout");
if (!is_stderr) {
@@ -917,14 +1033,15 @@ int channel_default_bufferize(ssh_channel channel,
* @see ssh_channel_request_shell()
* @see ssh_channel_request_exec()
*/
-int ssh_channel_open_session(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_open_session(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
return channel_open(channel,
"session",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
NULL);
}
@@ -944,14 +1061,15 @@ int ssh_channel_open_session(ssh_channel channel) {
*
* @see ssh_channel_open_forward()
*/
-int ssh_channel_open_auth_agent(ssh_channel channel){
- if(channel == NULL) {
+int ssh_channel_open_auth_agent(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
return channel_open(channel,
"auth-agent@openssh.com",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
NULL);
}
@@ -980,16 +1098,17 @@ int ssh_channel_open_auth_agent(ssh_channel channel){
*
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
- * use channel_read and channel_write for this.
+ * use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
- int remoteport, const char *sourcehost, int localport) {
+ int remoteport, const char *sourcehost, int localport)
+{
ssh_session session;
ssh_buffer payload = NULL;
ssh_string str = NULL;
int rc = SSH_ERROR;
- if(channel == NULL) {
+ if (channel == NULL) {
return rc;
}
@@ -1019,7 +1138,7 @@ int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
rc = channel_open(channel,
"direct-tcpip",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -1051,7 +1170,7 @@ error:
*
* @warning This function does not bind the local port and does not
* automatically forward the content of a socket to the channel.
- * You still have to use channel_read and channel_write for this.
+ * You still have to use ssh_channel_read and ssh_channel_write for this.
* @warning Requires support of OpenSSH for UNIX domain socket forwarding.
*/
int ssh_channel_open_forward_unix(ssh_channel channel,
@@ -1102,7 +1221,7 @@ int ssh_channel_open_forward_unix(ssh_channel channel,
rc = channel_open(channel,
"direct-streamlocal@openssh.com",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -1156,7 +1275,7 @@ void ssh_channel_free(ssh_channel channel)
channel->flags |= SSH_CHANNEL_FLAG_FREED_LOCAL;
/* The idea behind the flags is the following : it is well possible
- * that a client closes a channel that stills exists on the server side.
+ * that a client closes a channel that still exists on the server side.
* We definitively close the channel when we receive a close message *and*
* the user closed it.
*/
@@ -1249,7 +1368,7 @@ int ssh_channel_send_eof(ssh_channel channel)
rc = ssh_packet_send(session);
SSH_LOG(SSH_LOG_PACKET,
- "Sent a EOF on client channel (%d:%d)",
+ "Sent a EOF on client channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
if (rc != SSH_OK) {
@@ -1314,7 +1433,7 @@ int ssh_channel_close(ssh_channel channel)
rc = ssh_packet_send(session);
SSH_LOG(SSH_LOG_PACKET,
- "Sent a close on client channel (%d:%d)",
+ "Sent a close on client channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
@@ -1336,7 +1455,8 @@ error:
}
/* this termination function waits for a window growing condition */
-static int ssh_channel_waitwindow_termination(void *c){
+static int ssh_channel_waitwindow_termination(void *c)
+{
ssh_channel channel = (ssh_channel) c;
if (channel->remote_window > 0 ||
channel->session->session_state == SSH_SESSION_STATE_ERROR ||
@@ -1349,7 +1469,8 @@ static int ssh_channel_waitwindow_termination(void *c){
/* This termination function waits until the session is not in blocked status
* anymore, e.g. because of a key re-exchange.
*/
-static int ssh_waitsession_unblocked(void *s){
+static int ssh_waitsession_unblocked(void *s)
+{
ssh_session session = (ssh_session)s;
switch (session->session_state){
case SSH_SESSION_STATE_DH:
@@ -1369,8 +1490,9 @@ static int ssh_waitsession_unblocked(void *s){
* SSH_ERROR On error.
* SSH_AGAIN Timeout elapsed (or in nonblocking mode).
*/
-int ssh_channel_flush(ssh_channel channel){
- return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT);
+int ssh_channel_flush(ssh_channel channel)
+{
+ return ssh_blocking_flush(channel->session, SSH_TIMEOUT_DEFAULT);
}
static int channel_write_common(ssh_channel channel,
@@ -1380,7 +1502,6 @@ static int channel_write_common(ssh_channel channel,
ssh_session session;
uint32_t origlen = len;
size_t effectivelen;
- size_t maxpacketlen;
int rc;
if(channel == NULL) {
@@ -1393,20 +1514,14 @@ static int channel_write_common(ssh_channel channel,
}
if (len > INT_MAX) {
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Length (%u) is bigger than INT_MAX", len);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Length (%" PRIu32 ") is bigger than INT_MAX", len);
return SSH_ERROR;
}
- /*
- * Handle the max packet len from remote side, be nice
- * 10 bytes for the headers
- */
- maxpacketlen = channel->remote_maxpacket - 10;
-
if (channel->local_eof) {
ssh_set_error(session, SSH_REQUEST_DENIED,
- "Can't write to channel %d:%d after EOF was sent",
+ "Can't write to channel %" PRIu32 ":%" PRIu32 " after EOF was sent",
channel->local_channel,
channel->remote_channel);
return -1;
@@ -1430,14 +1545,14 @@ static int channel_write_common(ssh_channel channel,
}
while (len > 0) {
if (channel->remote_window < len) {
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Remote window is %d bytes. going to write %d bytes",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Remote window is %" PRIu32 " bytes. going to write %" PRIu32 " bytes",
channel->remote_window,
len);
- /* What happens when the channel window is zero? */
+ /* When the window is zero, wait for it to grow */
if(channel->remote_window == 0) {
/* nothing can be written */
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Wait for a growing window message...");
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_DEFAULT,
ssh_channel_waitwindow_termination,channel);
@@ -1448,12 +1563,17 @@ static int channel_write_common(ssh_channel channel,
goto out;
continue;
}
+ /* When the window is non-zero, accept data up to the window size */
effectivelen = MIN(len, channel->remote_window);
} else {
effectivelen = len;
}
- effectivelen = MIN(effectivelen, maxpacketlen);;
+ /*
+ * Like OpenSSH, don't subtract bytes for the header fields
+ * and allow to send a payload of remote_maxpacket length.
+ */
+ effectivelen = MIN(effectivelen, channel->remote_maxpacket);
rc = ssh_buffer_pack(session->out_buffer,
"bd",
@@ -1491,7 +1611,7 @@ static int channel_write_common(ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "channel_write wrote %ld bytes", (long int) effectivelen);
+ "ssh_channel_write wrote %ld bytes", (long int) effectivelen);
channel->remote_window -= effectivelen;
len -= effectivelen;
@@ -1519,7 +1639,7 @@ error:
/**
* @brief Get the remote window size.
*
- * This is the maximum amounts of bytes the remote side expects us to send
+ * This is the maximum amount of bytes the remote side expects us to send
* before growing the window again.
*
* @param[in] channel The channel to query.
@@ -1533,7 +1653,8 @@ error:
* @warning A zero return value means ssh_channel_write (default settings)
* will block until the window grows back.
*/
-uint32_t ssh_channel_window_size(ssh_channel channel) {
+uint32_t ssh_channel_window_size(ssh_channel channel)
+{
return channel->remote_window;
}
@@ -1550,8 +1671,9 @@ uint32_t ssh_channel_window_size(ssh_channel channel) {
*
* @see ssh_channel_read()
*/
-int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
- return channel_write_common(channel, data, len, 0);
+int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
+{
+ return channel_write_common(channel, data, len, 0);
}
/**
@@ -1563,8 +1685,9 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
*
* @see ssh_channel_is_closed()
*/
-int ssh_channel_is_open(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_is_open(ssh_channel channel)
+{
+ if (channel == NULL) {
return 0;
}
return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0);
@@ -1579,8 +1702,9 @@ int ssh_channel_is_open(ssh_channel channel) {
*
* @see ssh_channel_is_open()
*/
-int ssh_channel_is_closed(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_is_closed(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
return (channel->state != SSH_CHANNEL_STATE_OPEN || channel->session->alive == 0);
@@ -1593,18 +1717,16 @@ int ssh_channel_is_closed(ssh_channel channel) {
*
* @return 0 if there is no EOF, nonzero otherwise.
*/
-int ssh_channel_is_eof(ssh_channel channel) {
- if(channel == NULL) {
- return SSH_ERROR;
- }
- if ((channel->stdout_buffer &&
- ssh_buffer_get_len(channel->stdout_buffer) > 0) ||
- (channel->stderr_buffer &&
- ssh_buffer_get_len(channel->stderr_buffer) > 0)) {
- return 0;
- }
+int ssh_channel_is_eof(ssh_channel channel)
+{
+ if (channel == NULL) {
+ return SSH_ERROR;
+ }
+ if (ssh_channel_has_unread_data(channel)) {
+ return 0;
+ }
- return (channel->remote_eof != 0);
+ return (channel->remote_eof != 0);
}
/**
@@ -1618,11 +1740,12 @@ int ssh_channel_is_eof(ssh_channel channel) {
* in non-blocking mode.
* @see ssh_set_blocking()
*/
-void ssh_channel_set_blocking(ssh_channel channel, int blocking) {
- if(channel == NULL) {
- return;
- }
- ssh_set_blocking(channel->session,blocking);
+void ssh_channel_set_blocking(ssh_channel channel, int blocking)
+{
+ if (channel == NULL) {
+ return;
+ }
+ ssh_set_blocking(channel->session, blocking);
}
/**
@@ -1642,7 +1765,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){
}
SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_CHANNEL_SUCCESS on channel (%d:%d)",
+ "Received SSH_CHANNEL_SUCCESS on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
@@ -1650,6 +1773,12 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_success){
channel->request_state);
} else {
channel->request_state=SSH_CHANNEL_REQ_STATE_ACCEPTED;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_request_response_function,
+ channel->session,
+ channel);
}
return SSH_PACKET_USED;
@@ -1673,7 +1802,7 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){
}
SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_CHANNEL_FAILURE on channel (%d:%d)",
+ "Received SSH_CHANNEL_FAILURE on channel (%" PRIu32 ":%" PRIu32 ")",
channel->local_channel,
channel->remote_channel);
if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING){
@@ -1681,12 +1810,19 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_failure){
channel->request_state);
} else {
channel->request_state=SSH_CHANNEL_REQ_STATE_DENIED;
+
+ ssh_callbacks_execute_list(channel->callbacks,
+ ssh_channel_callbacks,
+ channel_request_response_function,
+ channel->session,
+ channel);
}
return SSH_PACKET_USED;
}
-static int ssh_channel_request_termination(void *c){
+static int ssh_channel_request_termination(void *c)
+{
ssh_channel channel = (ssh_channel)c;
if(channel->request_state != SSH_CHANNEL_REQ_STATE_PENDING ||
channel->session->session_state == SSH_SESSION_STATE_ERROR)
@@ -1696,7 +1832,8 @@ static int ssh_channel_request_termination(void *c){
}
static int channel_request(ssh_channel channel, const char *request,
- ssh_buffer buffer, int reply) {
+ ssh_buffer buffer, int reply)
+{
ssh_session session = channel->session;
int rc = SSH_ERROR;
int ret;
@@ -1757,7 +1894,7 @@ pending:
rc=SSH_ERROR;
break;
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Channel request %s success",request);
rc=SSH_OK;
break;
@@ -1782,7 +1919,7 @@ error:
/**
* @brief Request a pty with a specific type and size.
*
- * @param[in] channel The channel to sent the request.
+ * @param[in] channel The channel to send the request.
*
* @param[in] terminal The terminal type ("vt100, xterm,...").
*
@@ -1790,13 +1927,18 @@ error:
*
* @param[in] row The number of rows.
*
+ * @param[in] modes Encoded SSH terminal modes for the PTY
+ *
+ * @param[in] modes_len Number of bytes in 'modes'
+ *
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* to be done again.
*/
-int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
- int col, int row) {
+int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal,
+ int col, int row, const unsigned char* modes, size_t modes_len)
+{
ssh_session session;
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -1825,14 +1967,14 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
}
rc = ssh_buffer_pack(buffer,
- "sdddddb",
+ "sdddddP",
terminal,
col,
row,
0, /* pix */
0, /* pix */
- 1, /* add a 0byte string */
- 0);
+ (uint32_t)modes_len,
+ modes_len, modes);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
@@ -1846,6 +1988,23 @@ error:
return rc;
}
+int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
+ int col, int row)
+{
+ /* use modes from the current TTY */
+ unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
+ int rc = encode_current_tty_opts(modes_buf, sizeof(modes_buf));
+ if (rc < 0) {
+ return rc;
+ }
+ return ssh_channel_request_pty_size_modes(channel,
+ terminal,
+ col,
+ row,
+ modes_buf,
+ (size_t)rc);
+}
+
/**
* @brief Request a PTY.
*
@@ -1858,7 +2017,8 @@ error:
*
* @see ssh_channel_request_pty_size()
*/
-int ssh_channel_request_pty(ssh_channel channel) {
+int ssh_channel_request_pty(ssh_channel channel)
+{
return ssh_channel_request_pty_size(channel, "xterm", 80, 24);
}
@@ -1874,10 +2034,11 @@ int ssh_channel_request_pty(ssh_channel channel) {
* @return SSH_OK on success, SSH_ERROR if an error occurred.
*
* @warning Do not call it from a signal handler if you are not sure any other
- * libssh function using the same channel/session is running at same
- * time (not 100% threadsafe).
+ * libssh function using the same channel/session is running at the
+ * same time (not 100% threadsafe).
*/
-int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows) {
+int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows)
+{
ssh_session session = channel->session;
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -1916,8 +2077,9 @@ error:
* SSH_AGAIN if in nonblocking mode and call has
* to be done again.
*/
-int ssh_channel_request_shell(ssh_channel channel) {
- if(channel == NULL) {
+int ssh_channel_request_shell(ssh_channel channel)
+{
+ if (channel == NULL) {
return SSH_ERROR;
}
@@ -1938,7 +2100,8 @@ int ssh_channel_request_shell(ssh_channel channel) {
*
* @warning You normally don't have to call it for sftp, see sftp_new().
*/
-int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys) {
+int ssh_channel_request_subsystem(ssh_channel channel, const char *subsys)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -1987,14 +2150,16 @@ error:
*
* @note You should use sftp_new() which does this for you.
*/
-int ssh_channel_request_sftp( ssh_channel channel){
+int ssh_channel_request_sftp( ssh_channel channel)
+{
if(channel == NULL) {
return SSH_ERROR;
}
return ssh_channel_request_subsystem(channel, "sftp");
}
-static char *generate_cookie(void) {
+static char *generate_cookie(void)
+{
static const char *hex = "0123456789abcdef";
char s[36];
unsigned char rnd[16];
@@ -2018,7 +2183,7 @@ static char *generate_cookie(void) {
* @brief Sends the "x11-req" channel request over an existing session channel.
*
* This will enable redirecting the display of the remote X11 applications to
- * local X server over an secure tunnel.
+ * local X server over a secure tunnel.
*
* @param[in] channel An existing session channel where the remote X11
* applications are going to be executed.
@@ -2040,7 +2205,8 @@ static char *generate_cookie(void) {
* to be done again.
*/
int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
- const char *cookie, int screen_number) {
+ const char *cookie, int screen_number)
+{
ssh_buffer buffer = NULL;
char *c = NULL;
int rc = SSH_ERROR;
@@ -2091,7 +2257,8 @@ error:
}
static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
- int timeout_ms, int *destination_port) {
+ int timeout_ms, int *destination_port, char **originator, int *originator_port)
+{
#ifndef _WIN32
static const struct timespec ts = {
.tv_sec = 0,
@@ -2125,6 +2292,12 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
if(destination_port) {
*destination_port=msg->channel_request_open.destination_port;
}
+ if(originator) {
+ *originator=strdup(msg->channel_request_open.originator);
+ }
+ if(originator_port) {
+ *originator_port=msg->channel_request_open.originator_port;
+ }
ssh_message_free(msg);
return channel;
@@ -2155,8 +2328,9 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
* @return A newly created channel, or NULL if no X11 request from
* the server.
*/
-ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) {
- return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL);
+ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms)
+{
+ return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL, NULL, NULL);
}
/**
@@ -2225,7 +2399,8 @@ SSH_PACKET_CALLBACK(ssh_request_denied){
}
-static int ssh_global_request_termination(void *s){
+static int ssh_global_request_termination(void *s)
+{
ssh_session session = (ssh_session) s;
if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING ||
session->session_state == SSH_SESSION_STATE_ERROR)
@@ -2314,7 +2489,7 @@ pending:
}
switch(session->global_req_state){
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
- SSH_LOG(SSH_LOG_PROTOCOL, "Global request %s success",request);
+ SSH_LOG(SSH_LOG_DEBUG, "Global request %s success",request);
rc=SSH_OK;
break;
case SSH_CHANNEL_REQ_STATE_DENIED:
@@ -2404,18 +2579,21 @@ error:
}
/* DEPRECATED */
-int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port) {
+int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port)
+{
return ssh_channel_listen_forward(session, address, port, bound_port);
}
/* DEPRECATED */
-ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) {
- return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL);
+ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms)
+{
+ return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL, NULL, NULL);
}
/**
- * @brief Accept an incoming TCP/IP forwarding channel and get information
- * about incomming connection
+ * @brief Accept an incoming TCP/IP forwarding channel and get some information
+ * about incoming connection
+ *
* @param[in] session The ssh session to use.
*
* @param[in] timeout_ms A timeout in milliseconds.
@@ -2426,7 +2604,31 @@ ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) {
* the server
*/
ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) {
- return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port);
+ return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port, NULL, NULL);
+}
+
+/**
+ * @brief Accept an incoming TCP/IP forwarding channel and get information
+ * about incoming connection
+ *
+ * @param[in] session The ssh session to use.
+ *
+ * @param[in] timeout_ms A timeout in milliseconds.
+ *
+ * @param[out] destination_port A pointer to destination port or NULL.
+ *
+ * @param[out] originator A pointer to a pointer to a string of originator host or NULL.
+ * That the caller is responsible for to ssh_string_free_char().
+ *
+ * @param[out] originator_port A pointer to originator port or NULL.
+ *
+ * @return Newly created channel, or NULL if no incoming channel request from
+ * the server
+ *
+ * @see ssh_string_free_char()
+ */
+ssh_channel ssh_channel_open_forward_port(ssh_session session, int timeout_ms, int *destination_port, char **originator, int *originator_port) {
+ return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port, originator, originator_port);
}
/**
@@ -2476,7 +2678,8 @@ error:
}
/* DEPRECATED */
-int ssh_forward_cancel(ssh_session session, const char *address, int port) {
+int ssh_forward_cancel(ssh_session session, const char *address, int port)
+{
return ssh_channel_cancel_forward(session, address, port);
}
@@ -2495,7 +2698,8 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port) {
* to be done again.
* @warning Some environment variables may be refused by security reasons.
*/
-int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value) {
+int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2565,7 +2769,8 @@ error:
*
* @see ssh_channel_request_shell()
*/
-int ssh_channel_request_exec(ssh_channel channel, const char *cmd) {
+int ssh_channel_request_exec(ssh_channel channel, const char *cmd)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2609,10 +2814,6 @@ error:
* Sends a signal 'sig' to the remote process.
* Note, that remote system may not support signals concept.
* In such a case this request will be silently ignored.
- * Only SSH-v2 is supported (I'm not sure about SSH-v1).
- *
- * OpenSSH doesn't support signals yet, see:
- * https://bugzilla.mindrot.org/show_bug.cgi?id=1424
*
* @param[in] channel The channel to send signal.
*
@@ -2632,17 +2833,17 @@ error:
* SIGUSR1 -> USR1 \n
* SIGUSR2 -> USR2 \n
*
- * @return SSH_OK on success, SSH_ERROR if an error occurred
- * (including attempts to send signal via SSH-v1 session).
+ * @return SSH_OK on success, SSH_ERROR if an error occurred.
*/
-int ssh_channel_request_send_signal(ssh_channel channel, const char *sig) {
+int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
- if(channel == NULL) {
+ if (channel == NULL) {
return SSH_ERROR;
}
- if(sig == NULL) {
+ if (sig == NULL) {
ssh_set_error_invalid(channel->session);
return rc;
}
@@ -2672,16 +2873,15 @@ error:
* Sends a break signal to the remote process.
* Note, that remote system may not support breaks.
* In such a case this request will be silently ignored.
- * Only SSH-v2 is supported.
*
* @param[in] channel The channel to send the break to.
*
* @param[in] length The break-length in milliseconds to send.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
- * (including attempts to send signal via SSH-v1 session).
*/
-int ssh_channel_request_send_break(ssh_channel channel, uint32_t length) {
+int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -2714,7 +2914,7 @@ error:
*
* @param[in] channel The channel to read from.
*
- * @param[in] buffer The buffer which will get the data.
+ * @param[out] buffer The buffer which will get the data.
*
* @param[in] count The count of bytes to be read. If it is bigger than 0,
* the exact size will be read, else (bytes=0) it will
@@ -2729,9 +2929,10 @@ error:
* @see ssh_channel_read
*/
int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
- int is_stderr) {
+ int is_stderr)
+{
ssh_session session;
- char buffer_tmp[8192];
+ char *buffer_tmp = NULL;
int r;
uint32_t total=0;
@@ -2753,14 +2954,19 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
return r;
}
if(r > 0){
+ count = r;
+ buffer_tmp = ssh_buffer_allocate(buffer, count);
+ if (buffer_tmp == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
r=ssh_channel_read(channel, buffer_tmp, r, is_stderr);
if(r < 0){
+ ssh_buffer_pass_bytes_end(buffer, count);
return r;
}
- if(ssh_buffer_add_data(buffer,buffer_tmp,r) < 0){
- ssh_set_error_oom(session);
- r = SSH_ERROR;
- }
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(buffer, count - r);
return r;
}
@@ -2770,19 +2976,23 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
ssh_handle_packets(channel->session, SSH_TIMEOUT_INFINITE);
} while (r == 0);
}
+
+ buffer_tmp = ssh_buffer_allocate(buffer, count);
+ if (buffer_tmp == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
while(total < count){
- r=ssh_channel_read(channel, buffer_tmp, sizeof(buffer_tmp), is_stderr);
+ r=ssh_channel_read(channel, buffer_tmp, count - total, is_stderr);
if(r<0){
+ ssh_buffer_pass_bytes_end(buffer, count);
return r;
}
if(r==0){
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(buffer, count - total);
return total;
}
- if (ssh_buffer_add_data(buffer,buffer_tmp,r) < 0) {
- ssh_set_error_oom(session);
-
- return SSH_ERROR;
- }
total += r;
}
@@ -2791,13 +3001,13 @@ int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
struct ssh_channel_read_termination_struct {
ssh_channel channel;
- uint32_t count;
ssh_buffer buffer;
};
-static int ssh_channel_read_termination(void *s){
+static int ssh_channel_read_termination(void *s)
+{
struct ssh_channel_read_termination_struct *ctx = s;
- if (ssh_buffer_get_len(ctx->buffer) >= ctx->count ||
+ if (ssh_buffer_get_len(ctx->buffer) >= 1 ||
ctx->channel->remote_eof ||
ctx->channel->session->session_state == SSH_SESSION_STATE_ERROR)
return 1;
@@ -2805,28 +3015,25 @@ static int ssh_channel_read_termination(void *s){
return 0;
}
-/* TODO FIXME Fix the delayed close thing */
-/* TODO FIXME Fix the blocking behaviours */
+/* TODO: FIXME Fix the blocking behaviours */
/**
* @brief Reads data from a channel.
*
* @param[in] channel The channel to read from.
*
- * @param[in] dest The destination buffer which will get the data.
+ * @param[out] dest The destination buffer which will get the data.
*
* @param[in] count The count of bytes to be read.
*
* @param[in] is_stderr A boolean value to mark reading from the stderr flow.
*
* @return The number of bytes read, 0 on end of file or SSH_ERROR
- * on error. In nonblocking mode it Can return 0 if no data
+ * on error. In nonblocking mode it can return 0 if no data
* is available or SSH_AGAIN.
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
- * @warning The read function using a buffer has been renamed to
- * channel_read_buffer().
*/
int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
{
@@ -2842,7 +3049,7 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
*
* @param[in] channel The channel to read from.
*
- * @param[in] dest The destination buffer which will get the data.
+ * @param[out] dest The destination buffer which will get the data.
*
* @param[in] count The count of bytes to be read.
*
@@ -2857,8 +3064,6 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
- * @warning The read function using a buffer has been renamed to
- * channel_read_buffer().
*/
int ssh_channel_read_timeout(ssh_channel channel,
void *dest,
@@ -2891,28 +3096,17 @@ int ssh_channel_read_timeout(ssh_channel channel,
stdbuf=channel->stderr_buffer;
}
- /*
- * We may have problem if the window is too small to accept as much data
- * as asked
- */
SSH_LOG(SSH_LOG_PACKET,
- "Read (%d) buffered : %d bytes. Window: %d",
+ "Read (%" PRIu32 ") buffered : %" PRIu32 " bytes. Window: %" PRIu32,
count,
ssh_buffer_get_len(stdbuf),
channel->local_window);
- if (count > ssh_buffer_get_len(stdbuf) + channel->local_window) {
- if (grow_window(session, channel, count - ssh_buffer_get_len(stdbuf)) < 0) {
- return -1;
- }
- }
-
/* block reading until at least one byte has been read
* and ignore the trivial case count=0
*/
ctx.channel = channel;
ctx.buffer = stdbuf;
- ctx.count = 1;
if (timeout_ms < SSH_TIMEOUT_DEFAULT) {
timeout_ms = SSH_TIMEOUT_INFINITE;
@@ -2950,11 +3144,14 @@ int ssh_channel_read_timeout(ssh_channel channel,
if (channel->counter != NULL) {
channel->counter->in_bytes += len;
}
- /* Authorize some buffering while userapp is busy */
- if (channel->local_window < WINDOWLIMIT) {
- if (grow_window(session, channel, 0) < 0) {
- return -1;
- }
+ /* Try completing the delayed_close */
+ if (channel->delayed_close && !ssh_channel_has_unread_data(channel)) {
+ channel->state = SSH_CHANNEL_STATE_CLOSED;
+ }
+
+ rc = grow_window(session, channel);
+ if (rc == SSH_ERROR) {
+ return -1;
}
return len;
@@ -2964,20 +3161,18 @@ int ssh_channel_read_timeout(ssh_channel channel,
* @brief Do a nonblocking read on the channel.
*
* A nonblocking read on the specified channel. it will return <= count bytes of
- * data read atomically.
+ * data read atomically. It will also trigger any callbacks set on the channel.
*
* @param[in] channel The channel to read from.
*
- * @param[in] dest A pointer to a destination buffer.
+ * @param[out] dest A pointer to a destination buffer.
*
* @param[in] count The count of bytes of data to be read.
*
* @param[in] is_stderr A boolean to select the stderr stream.
*
- * @return The number of bytes read, 0 if nothing is available or
- * SSH_ERROR on error.
- *
- * @warning Don't forget to check for EOF as it would return 0 here.
+ * @return The number of bytes read (0 if nothing is available),
+ * SSH_ERROR on error, and SSH_EOF if the channel is EOF.
*
* @see ssh_channel_is_eof()
*/
@@ -2987,7 +3182,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
int is_stderr)
{
ssh_session session;
- ssize_t to_read;
+ uint32_t to_read;
int rc;
int blocking;
@@ -3001,22 +3196,24 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
session = channel->session;
- to_read = ssh_channel_poll(channel, is_stderr);
+ rc = ssh_channel_poll(channel, is_stderr);
- if (to_read <= 0) {
+ if (rc <= 0) {
if (session->session_state == SSH_SESSION_STATE_ERROR){
return SSH_ERROR;
}
- return to_read; /* may be an error code */
+ return rc; /* may be an error code */
}
- if ((size_t)to_read > count) {
- to_read = (ssize_t)count;
+ to_read = (unsigned int)rc;
+
+ if (to_read > count) {
+ to_read = count;
}
blocking = ssh_is_blocking(session);
ssh_set_blocking(session, 0);
- rc = ssh_channel_read(channel, dest, (uint32_t)to_read, is_stderr);
+ rc = ssh_channel_read(channel, dest, to_read, is_stderr);
ssh_set_blocking(session,blocking);
return rc;
@@ -3031,15 +3228,18 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
*
* @return The number of bytes available for reading, 0 if nothing
* is available or SSH_ERROR on error.
+ * When a channel is freed the function returns
+ * SSH_ERROR immediately.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
*
* @see ssh_channel_is_eof()
*/
-int ssh_channel_poll(ssh_channel channel, int is_stderr){
+int ssh_channel_poll(ssh_channel channel, int is_stderr)
+{
ssh_buffer stdbuf;
- if(channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
@@ -3085,6 +3285,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){
* SSH_ERROR on error.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
+ * When a channel is freed the function returns SSH_ERROR immediately.
*
* @see ssh_channel_is_eof()
*/
@@ -3096,7 +3297,7 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
size_t len;
int rc;
- if (channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
@@ -3108,7 +3309,6 @@ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
}
ctx.buffer = stdbuf;
ctx.channel = channel;
- ctx.count = 1;
rc = ssh_handle_packets_termination(channel->session,
timeout,
ssh_channel_read_termination,
@@ -3148,15 +3348,17 @@ out:
*
* @return The session pointer.
*/
-ssh_session ssh_channel_get_session(ssh_channel channel) {
- if(channel == NULL) {
+ssh_session ssh_channel_get_session(ssh_channel channel)
+{
+ if (channel == NULL) {
return NULL;
}
return channel->session;
}
-static int ssh_channel_exit_status_termination(void *c){
+static int ssh_channel_exit_status_termination(void *c)
+{
ssh_channel channel = c;
if(channel->exit_status != -1 ||
/* When a channel is closed, no exit status message can
@@ -3178,15 +3380,18 @@ static int ssh_channel_exit_status_termination(void *c){
* (yet), or SSH_ERROR on error.
* @warning This function may block until a timeout (or never)
* if the other side is not willing to close the channel.
+ * When a channel is freed the function returns
+ * SSH_ERROR immediately.
*
* If you're looking for an async handling of this register a callback for the
* exit status.
*
* @see ssh_channel_exit_status_callback
*/
-int ssh_channel_get_exit_status(ssh_channel channel) {
+int ssh_channel_get_exit_status(ssh_channel channel)
+{
int rc;
- if(channel == NULL) {
+ if ((channel == NULL) || (channel->flags & SSH_CHANNEL_FLAG_FREED_LOCAL)) {
return SSH_ERROR;
}
rc = ssh_handle_packets_termination(channel->session,
@@ -3208,8 +3413,11 @@ int ssh_channel_get_exit_status(ssh_channel channel) {
* This is made in two parts: protocol select and network select. The protocol
* select does not use the network functions at all
*/
-static int channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans,
- ssh_channel *echans, ssh_channel *rout, ssh_channel *wout, ssh_channel *eout) {
+static int
+channel_protocol_select(ssh_channel *rchans, ssh_channel *wchans,
+ ssh_channel *echans, ssh_channel *rout,
+ ssh_channel *wout, ssh_channel *eout)
+{
ssh_channel chan;
int i;
int j = 0;
@@ -3289,7 +3497,8 @@ static size_t count_ptrs(ssh_channel *ptrs)
* function, or SSH_ERROR on error.
*/
int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
- ssh_channel *exceptchans, struct timeval * timeout) {
+ ssh_channel *exceptchans, struct timeval * timeout)
+{
ssh_channel *rchans, *wchans, *echans;
ssh_channel dummy = NULL;
ssh_event event = NULL;
@@ -3393,9 +3602,15 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
firstround=0;
} while(1);
- memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel ));
- memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel ));
- memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel ));
+ if (readchans != &dummy) {
+ memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel));
+ }
+ if (writechans != &dummy) {
+ memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel));
+ }
+ if (exceptchans != &dummy) {
+ memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel));
+ }
SAFE_FREE(rchans);
SAFE_FREE(wchans);
SAFE_FREE(echans);
@@ -3423,7 +3638,8 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
* @param[in] counter Counter for bytes handled by the channel.
*/
void ssh_channel_set_counter(ssh_channel channel,
- ssh_counter counter) {
+ ssh_counter counter)
+{
if (channel != NULL) {
channel->counter = counter;
}
@@ -3442,8 +3658,9 @@ void ssh_channel_set_counter(ssh_channel channel,
*
* @see ssh_channel_read()
*/
-int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
- return channel_write_common(channel, data, len, 1);
+int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len)
+{
+ return channel_write_common(channel, data, len, 1);
}
#if WITH_SERVER
@@ -3470,10 +3687,11 @@ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len
*
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
- * use channel_read and channel_write for this.
+ * use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
- int remoteport, const char *sourcehost, int localport) {
+ int remoteport, const char *sourcehost, int localport)
+{
ssh_session session;
ssh_buffer payload = NULL;
int rc = SSH_ERROR;
@@ -3508,7 +3726,7 @@ int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost
pending:
rc = channel_open(channel,
"forwarded-tcpip",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -3533,10 +3751,11 @@ error:
* to be done again.
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
- * use channel_read and channel_write for this.
+ * use shh_channel_read and ssh_channel_write for this.
*/
-int ssh_channel_open_x11(ssh_channel channel,
- const char *orig_addr, int orig_port) {
+int ssh_channel_open_x11(ssh_channel channel,
+ const char *orig_addr, int orig_port)
+{
ssh_session session;
ssh_buffer payload = NULL;
int rc = SSH_ERROR;
@@ -3570,7 +3789,7 @@ int ssh_channel_open_x11(ssh_channel channel,
pending:
rc = channel_open(channel,
"x11",
- CHANNEL_INITIAL_WINDOW,
+ WINDOW_DEFAULT,
CHANNEL_MAX_PACKET,
payload);
@@ -3585,16 +3804,15 @@ error:
*
* Sends the exit status to the remote process (as described in RFC 4254,
* section 6.10).
- * Only SSH-v2 is supported (I'm not sure about SSH-v1).
*
* @param[in] channel The channel to send exit status.
*
* @param[in] exit_status The exit status to send
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
- * (including attempts to send exit status via SSH-v1 session).
*/
-int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status) {
+int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -3626,7 +3844,6 @@ error:
* This sends the exit status of the remote process.
* Note, that remote system may not support signals concept.
* In such a case this request will be silently ignored.
- * Only SSH-v2 is supported (I'm not sure about SSH-v1).
*
* @param[in] channel The channel to send signal.
*
@@ -3637,10 +3854,10 @@ error:
* @param[in] lang The language used in the message (format: RFC 3066)
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
- * (including attempts to send signal via SSH-v1 session).
*/
int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig,
- int core, const char *errmsg, const char *lang) {
+ int core, const char *errmsg, const char *lang)
+{
ssh_buffer buffer = NULL;
int rc = SSH_ERROR;
@@ -3677,4 +3894,4 @@ error:
#endif
-/* @} */
+/** @} */
diff --git a/src/client.c b/src/client.c
index 4610b787..b3d0fe62 100644
--- a/src/client.c
+++ b/src/client.c
@@ -60,7 +60,8 @@
* @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR
* @param user is a pointer to session
*/
-static void socket_callback_connected(int code, int errno_code, void *user){
+static void socket_callback_connected(int code, int errno_code, void *user)
+{
ssh_session session=(ssh_session)user;
if (session->session_state != SSH_SESSION_STATE_CONNECTING &&
@@ -72,12 +73,14 @@ static void socket_callback_connected(int code, int errno_code, void *user){
return;
}
- SSH_LOG(SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code);
+ SSH_LOG(SSH_LOG_TRACE,"Socket connection callback: %d (%d)",code, errno_code);
if(code == SSH_SOCKET_CONNECTED_OK)
session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED;
else {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
session->session_state=SSH_SESSION_STATE_ERROR;
- ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code));
+ ssh_set_error(session,SSH_FATAL,"%s",
+ ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX));
}
session->ssh_connection_callback(session);
}
@@ -93,12 +96,12 @@ static void socket_callback_connected(int code, int errno_code, void *user){
* @param user is a pointer to session
* @returns Number of bytes processed, or zero if the banner is not complete.
*/
-static int callback_receive_banner(const void *data, size_t len, void *user)
+static size_t callback_receive_banner(const void *data, size_t len, void *user)
{
char *buffer = (char *)data;
- ssh_session session=(ssh_session) user;
+ ssh_session session = (ssh_session) user;
char *str = NULL;
- size_t i;
+ uint32_t i;
int ret=0;
if (session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED) {
@@ -106,7 +109,7 @@ static int callback_receive_banner(const void *data, size_t len, void *user)
"Wrong state in callback_receive_banner : %d",
session->session_state);
- return SSH_ERROR;
+ return 0;
}
for (i = 0; i < len; ++i) {
#ifdef WITH_PCAP
@@ -243,10 +246,13 @@ end:
* @warning this function returning is no proof that DH handshake is
* completed
*/
-static int dh_handshake(ssh_session session) {
-
+int dh_handshake(ssh_session session)
+{
int rc = SSH_AGAIN;
+ SSH_LOG(SSH_LOG_TRACE, "dh_handshake_state = %d, kex_type = %d",
+ session->dh_handshake_state, session->next_crypto->kex_type);
+
switch (session->dh_handshake_state) {
case DH_STATE_INIT:
switch(session->next_crypto->kex_type){
@@ -299,18 +305,25 @@ static int dh_handshake(ssh_session session) {
return rc;
}
-static int ssh_service_request_termination(void *s){
- ssh_session session = (ssh_session)s;
- if(session->session_state == SSH_SESSION_STATE_ERROR ||
- session->auth.service_state != SSH_AUTH_SERVICE_SENT)
- return 1;
- else
- return 0;
+static int ssh_service_request_termination(void *s)
+{
+ ssh_session session = (ssh_session)s;
+
+ if (session->session_state == SSH_SESSION_STATE_ERROR ||
+ session->auth.service_state != SSH_AUTH_SERVICE_SENT)
+ return 1;
+ else
+ return 0;
}
/**
- * @internal
+ * @addtogroup libssh_session
*
+ * @{
+ */
+
+/**
+ * @internal
* @brief Request a service from the SSH server.
*
* Service requests are for example: ssh-userauth, ssh-connection, etc.
@@ -323,8 +336,9 @@ static int ssh_service_request_termination(void *s){
* @return SSH_AGAIN No response received yet
* @bug actually only works with ssh-userauth
*/
-int ssh_service_request(ssh_session session, const char *service) {
- int rc=SSH_ERROR;
+int ssh_service_request(ssh_session session, const char *service)
+{
+ int rc = SSH_ERROR;
if(session->auth.service_state != SSH_AUTH_SERVICE_NONE)
goto pending;
@@ -371,12 +385,6 @@ pending:
}
/**
- * @addtogroup libssh_session
- *
- * @{
- */
-
-/**
* @internal
*
* @brief A function to be called each time a step has been done in the
@@ -386,111 +394,117 @@ static void ssh_client_connection_callback(ssh_session session)
{
int rc;
- switch(session->session_state) {
- case SSH_SESSION_STATE_NONE:
- case SSH_SESSION_STATE_CONNECTING:
- break;
- case SSH_SESSION_STATE_SOCKET_CONNECTED:
- ssh_set_fd_towrite(session);
- ssh_send_banner(session, 0);
+ SSH_LOG(SSH_LOG_DEBUG, "session_state=%d", session->session_state);
- break;
- case SSH_SESSION_STATE_BANNER_RECEIVED:
- if (session->serverbanner == NULL) {
- goto error;
- }
- set_status(session, 0.4f);
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SSH server banner: %s", session->serverbanner);
+ switch (session->session_state) {
+ case SSH_SESSION_STATE_NONE:
+ case SSH_SESSION_STATE_CONNECTING:
+ break;
+ case SSH_SESSION_STATE_SOCKET_CONNECTED:
+ ssh_set_fd_towrite(session);
+ ssh_send_banner(session, 0);
- /* Here we analyze the different protocols the server allows. */
- rc = ssh_analyze_banner(session, 0);
- if (rc < 0) {
- ssh_set_error(session, SSH_FATAL,
- "No version of SSH protocol usable (banner: %s)",
- session->serverbanner);
- goto error;
- }
+ break;
+ case SSH_SESSION_STATE_BANNER_RECEIVED:
+ if (session->serverbanner == NULL) {
+ goto error;
+ }
+ set_status(session, 0.4f);
+ SSH_LOG(SSH_LOG_DEBUG, "SSH server banner: %s", session->serverbanner);
- ssh_packet_register_socket_callback(session, session->socket);
+ /* Here we analyze the different protocols the server allows. */
+ rc = ssh_analyze_banner(session, 0);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "No version of SSH protocol usable (banner: %s)",
+ session->serverbanner);
+ goto error;
+ }
+
+ ssh_packet_register_socket_callback(session, session->socket);
+
+ ssh_packet_set_default_callbacks(session);
+ session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
+ rc = ssh_set_client_kex(session);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ rc = ssh_send_kex(session);
+ if (rc < 0) {
+ goto error;
+ }
+ set_status(session, 0.5f);
- ssh_packet_set_default_callbacks(session);
- session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
+ break;
+ case SSH_SESSION_STATE_INITIAL_KEX:
+ /* TODO: This state should disappear in favor of get_key handle */
+ break;
+ case SSH_SESSION_STATE_KEXINIT_RECEIVED:
+ set_status(session, 0.6f);
+ ssh_list_kex(&session->next_crypto->server_kex);
+ if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
+ /* in rekeying state if next_crypto client_kex might be empty */
rc = ssh_set_client_kex(session);
if (rc != SSH_OK) {
goto error;
}
- rc = ssh_send_kex(session, 0);
+ rc = ssh_send_kex(session);
if (rc < 0) {
goto error;
}
- set_status(session, 0.5f);
-
- break;
- case SSH_SESSION_STATE_INITIAL_KEX:
- /* TODO: This state should disappear in favor of get_key handle */
- break;
- case SSH_SESSION_STATE_KEXINIT_RECEIVED:
- set_status(session,0.6f);
- ssh_list_kex(&session->next_crypto->server_kex);
- if (session->next_crypto->client_kex.methods[0] == NULL) {
- /* in rekeying state if next_crypto client_kex is empty */
- rc = ssh_set_client_kex(session);
- if (rc != SSH_OK) {
- goto error;
- }
- rc = ssh_send_kex(session, 0);
- if (rc < 0) {
- goto error;
- }
- }
- if (ssh_kex_select_methods(session) == SSH_ERROR)
- goto error;
- set_status(session,0.8f);
- session->session_state=SSH_SESSION_STATE_DH;
- if (dh_handshake(session) == SSH_ERROR) {
- goto error;
- }
- FALL_THROUGH;
- case SSH_SESSION_STATE_DH:
- if(session->dh_handshake_state==DH_STATE_FINISHED){
- set_status(session,1.0f);
- session->connected = 1;
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
- session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
- else
- session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
- }
- break;
- case SSH_SESSION_STATE_AUTHENTICATING:
- break;
- case SSH_SESSION_STATE_ERROR:
+ }
+ if (ssh_kex_select_methods(session) == SSH_ERROR)
goto error;
- default:
- ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
+ set_status(session, 0.8f);
+ session->session_state = SSH_SESSION_STATE_DH;
+
+ /* If the init packet was already sent in previous step, this will be no
+ * operation */
+ if (dh_handshake(session) == SSH_ERROR) {
+ goto error;
+ }
+ FALL_THROUGH;
+ case SSH_SESSION_STATE_DH:
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
+ set_status(session, 1.0f);
+ session->connected = 1;
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
+ } else {
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATING;
+ }
+ }
+ break;
+ case SSH_SESSION_STATE_AUTHENTICATING:
+ break;
+ case SSH_SESSION_STATE_ERROR:
+ goto error;
+ default:
+ ssh_set_error(session, SSH_FATAL, "Invalid state %d",
+ session->session_state);
}
return;
error:
- ssh_socket_close(session->socket);
- session->alive = 0;
- session->session_state=SSH_SESSION_STATE_ERROR;
-
+ ssh_session_socket_close(session);
+ SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(session));
}
/** @internal
* @brief describe under which conditions the ssh_connect function may stop
*/
-static int ssh_connect_termination(void *user){
- ssh_session session = (ssh_session)user;
- switch(session->session_state){
+static int ssh_connect_termination(void *user)
+{
+ ssh_session session = (ssh_session)user;
+
+ switch (session->session_state) {
case SSH_SESSION_STATE_ERROR:
case SSH_SESSION_STATE_AUTHENTICATING:
case SSH_SESSION_STATE_DISCONNECTED:
- return 1;
+ return 1;
default:
- return 0;
- }
+ return 0;
+ }
}
/**
@@ -558,7 +572,7 @@ int ssh_connect(ssh_session session)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"libssh %s, using threading %s",
ssh_copyright(),
ssh_threads_get_type());
@@ -593,7 +607,7 @@ int ssh_connect(ssh_session session)
set_status(session, 0.2f);
session->alive = 1;
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Socket connecting, now waiting for the callbacks to work");
pending:
@@ -649,12 +663,13 @@ pending:
*
* @return A newly allocated string with the banner, NULL on error.
*/
-char *ssh_get_issue_banner(ssh_session session) {
- if (session == NULL || session->banner == NULL) {
- return NULL;
- }
+char *ssh_get_issue_banner(ssh_session session)
+{
+ if (session == NULL || session->banner == NULL) {
+ return NULL;
+ }
- return ssh_string_to_char(session->banner);
+ return ssh_string_to_char(session->banner);
}
/**
@@ -675,102 +690,185 @@ char *ssh_get_issue_banner(ssh_session session) {
* }
* @endcode
*/
-int ssh_get_openssh_version(ssh_session session) {
- if (session == NULL) {
- return 0;
- }
+int ssh_get_openssh_version(ssh_session session)
+{
+ if (session == NULL) {
+ return 0;
+ }
+
+ return session->openssh;
+}
+
+/**
+ * @brief Most SSH connections will only ever request a single session, but an
+ * attacker may abuse a running ssh client to surreptitiously open
+ * additional sessions under their control. OpenSSH provides a global
+ * request "no-more-sessions@openssh.com" to mitigate this attack.
+ *
+ * @param[in] session The SSH session to use.
+ *
+ * @returns SSH_OK on success, SSH_ERROR on error.
+ * @returns SSH_AGAIN, if the session is in nonblocking mode,
+ * and call must be done again.
+ */
+int ssh_request_no_more_sessions(ssh_session session)
+{
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
- return session->openssh;
+ return ssh_global_request(session, "no-more-sessions@openssh.com", NULL, 1);
}
/**
+ * @brief Add disconnect message when ssh_session is disconnected
+ * To add a disconnect message to give peer a better hint.
+ * @param session The SSH session to use.
+ * @param message The message to send after the session is disconnected.
+ * If no message is passed then a default message i.e
+ * "Bye Bye" will be sent.
+ */
+int
+ssh_session_set_disconnect_message(ssh_session session, const char *message)
+{
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ if (message == NULL || strlen(message) == 0) {
+ SAFE_FREE(session->disconnect_message); //To free any message set earlier.
+ session->disconnect_message = strdup("Bye Bye") ;
+ if (session->disconnect_message == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+ }
+ SAFE_FREE(session->disconnect_message); //To free any message set earlier.
+ session->disconnect_message = strdup(message);
+ if (session->disconnect_message == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+
+/**
* @brief Disconnect from a session (client or server).
+ *
* The session can then be reused to open a new session.
*
+ * @note Note that this function won't close the socket if it was set with
+ * ssh_options_set and SSH_OPTIONS_FD. You're responsible for closing the
+ * socket. This is new behavior in libssh 0.10.
+ *
* @param[in] session The SSH session to use.
*/
-void ssh_disconnect(ssh_session session) {
- struct ssh_iterator *it;
- int rc;
+void
+ssh_disconnect(ssh_session session)
+{
+ struct ssh_iterator *it;
+ int rc;
- if (session == NULL) {
- return;
- }
+ if (session == NULL) {
+ return;
+ }
- if (session->socket != NULL && ssh_socket_is_open(session->socket)) {
- rc = ssh_buffer_pack(session->out_buffer,
- "bdss",
- SSH2_MSG_DISCONNECT,
- SSH2_DISCONNECT_BY_APPLICATION,
- "Bye Bye",
- ""); /* language tag */
- if (rc != SSH_OK){
- ssh_set_error_oom(session);
- goto error;
+ if (session->disconnect_message == NULL) {
+ session->disconnect_message = strdup("Bye Bye") ;
+ if (session->disconnect_message == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ }
+
+ if (session->socket != NULL && ssh_socket_is_open(session->socket)) {
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdss",
+ SSH2_MSG_DISCONNECT,
+ SSH2_DISCONNECT_BY_APPLICATION,
+ session->disconnect_message,
+ ""); /* language tag */
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+
+ ssh_packet_send(session);
+ ssh_session_socket_close(session);
}
- ssh_packet_send(session);
- ssh_socket_close(session->socket);
- }
error:
- session->recv_seq = 0;
- session->send_seq = 0;
- session->alive = 0;
- if (session->socket != NULL){
- ssh_socket_reset(session->socket);
- }
- session->opts.fd = SSH_INVALID_SOCKET;
- session->session_state=SSH_SESSION_STATE_DISCONNECTED;
+ session->recv_seq = 0;
+ session->send_seq = 0;
+ session->alive = 0;
+ if (session->socket != NULL){
+ ssh_socket_reset(session->socket);
+ }
+ session->opts.fd = SSH_INVALID_SOCKET;
+ session->session_state = SSH_SESSION_STATE_DISCONNECTED;
+ session->pending_call_state = SSH_PENDING_CALL_NONE;
- while ((it=ssh_list_get_iterator(session->channels)) != NULL) {
- ssh_channel_do_free(ssh_iterator_value(ssh_channel,it));
- ssh_list_remove(session->channels, it);
- }
- if(session->current_crypto){
- crypto_free(session->current_crypto);
- session->current_crypto=NULL;
- }
- if (session->next_crypto) {
- crypto_free(session->next_crypto);
- session->next_crypto = crypto_new();
- if (session->next_crypto == NULL) {
- ssh_set_error_oom(session);
+ while ((it = ssh_list_get_iterator(session->channels)) != NULL) {
+ ssh_channel_do_free(ssh_iterator_value(ssh_channel, it));
+ ssh_list_remove(session->channels, it);
}
- }
- if (session->in_buffer) {
- ssh_buffer_reinit(session->in_buffer);
- }
- if (session->out_buffer) {
- ssh_buffer_reinit(session->out_buffer);
- }
- if (session->in_hashbuf) {
- ssh_buffer_reinit(session->in_hashbuf);
- }
- if (session->out_hashbuf) {
- ssh_buffer_reinit(session->out_hashbuf);
- }
- session->auth.supported_methods = 0;
- SAFE_FREE(session->serverbanner);
- SAFE_FREE(session->clientbanner);
-
- if(session->ssh_message_list){
- ssh_message msg;
- while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list))
- != NULL){
- ssh_message_free(msg);
- }
- ssh_list_free(session->ssh_message_list);
- session->ssh_message_list=NULL;
- }
+ if (session->current_crypto) {
+ crypto_free(session->current_crypto);
+ session->current_crypto = NULL;
+ }
+ if (session->next_crypto) {
+ crypto_free(session->next_crypto);
+ session->next_crypto = crypto_new();
+ if (session->next_crypto == NULL) {
+ ssh_set_error_oom(session);
+ }
+ }
+ if (session->in_buffer) {
+ ssh_buffer_reinit(session->in_buffer);
+ }
+ if (session->out_buffer) {
+ ssh_buffer_reinit(session->out_buffer);
+ }
+ if (session->in_hashbuf) {
+ ssh_buffer_reinit(session->in_hashbuf);
+ }
+ if (session->out_hashbuf) {
+ ssh_buffer_reinit(session->out_hashbuf);
+ }
+ session->auth.supported_methods = 0;
+ SAFE_FREE(session->serverbanner);
+ SAFE_FREE(session->clientbanner);
+ SAFE_FREE(session->disconnect_message);
- if (session->packet_callbacks){
- ssh_list_free(session->packet_callbacks);
- session->packet_callbacks=NULL;
- }
+ if (session->ssh_message_list) {
+ ssh_message msg = NULL;
+
+ while ((msg = ssh_list_pop_head(ssh_message,
+ session->ssh_message_list)) != NULL) {
+ ssh_message_free(msg);
+ }
+ ssh_list_free(session->ssh_message_list);
+ session->ssh_message_list = NULL;
+ }
+
+ if (session->packet_callbacks) {
+ ssh_list_free(session->packet_callbacks);
+ session->packet_callbacks = NULL;
+ }
}
-const char *ssh_copyright(void) {
- return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2019 "
+/**
+ * @brief Copyright information
+ *
+ * Returns copyright information
+ *
+ * @returns SSH_STRING copyright
+ */
+const char *ssh_copyright(void)
+{
+ return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2024 "
"Aris Adamantiadis, Andreas Schneider "
"and libssh contributors. "
"Distributed under the LGPL, please refer to COPYING "
diff --git a/src/config.c b/src/config.c
index 79a64339..7135c3b1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -48,7 +48,9 @@
#include "libssh/misc.h"
#include "libssh/options.h"
+#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 1024
+#endif
struct ssh_config_keyword_table_s {
const char *name;
@@ -66,7 +68,6 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "macs", SOC_MACS },
{ "compression", SOC_COMPRESSION },
{ "connecttimeout", SOC_TIMEOUT },
- { "protocol", SOC_PROTOCOL },
{ "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK },
{ "userknownhostsfile", SOC_KNOWNHOSTS },
{ "proxycommand", SOC_PROXYCOMMAND },
@@ -79,7 +80,6 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "loglevel", SOC_LOGLEVEL},
{ "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS},
{ "kexalgorithms", SOC_KEXALGORITHMS},
- { "mac", SOC_UNSUPPORTED}, /* SSHv1 */
{ "gssapiauthentication", SOC_GSSAPIAUTHENTICATION},
{ "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION},
{ "passwordauthentication", SOC_PASSWORDAUTHENTICATION},
@@ -92,24 +92,19 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "canonicalizehostname", SOC_UNSUPPORTED},
{ "canonicalizemaxdots", SOC_UNSUPPORTED},
{ "canonicalizepermittedcnames", SOC_UNSUPPORTED},
- { "certificatefile", SOC_UNSUPPORTED},
- { "challengeresponseauthentication", SOC_UNSUPPORTED},
+ { "certificatefile", SOC_CERTIFICATE},
+ { "kbdinteractiveauthentication", SOC_UNSUPPORTED},
{ "checkhostip", SOC_UNSUPPORTED},
- { "cipher", SOC_UNSUPPORTED}, /* SSHv1 */
- { "compressionlevel", SOC_UNSUPPORTED}, /* SSHv1 */
{ "connectionattempts", SOC_UNSUPPORTED},
{ "enablesshkeysign", SOC_UNSUPPORTED},
{ "fingerprinthash", SOC_UNSUPPORTED},
{ "forwardagent", SOC_UNSUPPORTED},
- { "gssapikeyexchange", SOC_UNSUPPORTED},
- { "gssapirenewalforcesrekey", SOC_UNSUPPORTED},
- { "gssapitrustdns", SOC_UNSUPPORTED},
{ "hashknownhosts", SOC_UNSUPPORTED},
{ "hostbasedauthentication", SOC_UNSUPPORTED},
- { "hostbasedkeytypes", SOC_UNSUPPORTED},
+ { "hostbasedacceptedalgorithms", SOC_UNSUPPORTED},
{ "hostkeyalias", SOC_UNSUPPORTED},
- { "identitiesonly", SOC_UNSUPPORTED},
- { "identityagent", SOC_UNSUPPORTED},
+ { "identitiesonly", SOC_IDENTITIESONLY},
+ { "identityagent", SOC_IDENTITYAGENT},
{ "ipqos", SOC_UNSUPPORTED},
{ "kbdinteractivedevices", SOC_UNSUPPORTED},
{ "nohostauthenticationforlocalhost", SOC_UNSUPPORTED},
@@ -118,12 +113,10 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "preferredauthentications", SOC_UNSUPPORTED},
{ "proxyjump", SOC_PROXYJUMP},
{ "proxyusefdpass", SOC_UNSUPPORTED},
- { "pubkeyacceptedtypes", SOC_PUBKEYACCEPTEDTYPES},
+ { "pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES},
{ "rekeylimit", SOC_REKEYLIMIT},
{ "remotecommand", SOC_UNSUPPORTED},
{ "revokedhostkeys", SOC_UNSUPPORTED},
- { "rhostsrsaauthentication", SOC_UNSUPPORTED},
- { "rsaauthentication", SOC_UNSUPPORTED}, /* SSHv1 */
{ "serveralivecountmax", SOC_UNSUPPORTED},
{ "serveraliveinterval", SOC_UNSUPPORTED},
{ "streamlocalbindmask", SOC_UNSUPPORTED},
@@ -131,13 +124,12 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "syslogfacility", SOC_UNSUPPORTED},
{ "tcpkeepalive", SOC_UNSUPPORTED},
{ "updatehostkeys", SOC_UNSUPPORTED},
- { "useprivilegedport", SOC_UNSUPPORTED},
{ "verifyhostkeydns", SOC_UNSUPPORTED},
{ "visualhostkey", SOC_UNSUPPORTED},
{ "clearallforwardings", SOC_NA},
- { "controlmaster", SOC_NA},
+ { "controlmaster", SOC_CONTROLMASTER},
{ "controlpersist", SOC_NA},
- { "controlpath", SOC_NA},
+ { "controlpath", SOC_CONTROLPATH},
{ "dynamicforward", SOC_NA},
{ "escapechar", SOC_NA},
{ "exitonforwardfailure", SOC_NA},
@@ -155,7 +147,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "tunnel", SOC_NA},
{ "tunneldevice", SOC_NA},
{ "xauthlocation", SOC_NA},
- { "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDTYPES},
+ { "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES},
{ NULL, SOC_UNKNOWN }
};
@@ -189,7 +181,7 @@ static struct ssh_config_match_keyword_table_s ssh_config_match_keyword_table[]
};
static int ssh_config_parse_line(ssh_session session, const char *line,
- unsigned int count, int *parsing);
+ unsigned int count, int *parsing, unsigned int depth, bool global);
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
int i;
@@ -203,16 +195,26 @@ static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
return SOC_UNKNOWN;
}
+#define LIBSSH_CONF_MAX_DEPTH 16
static void
local_parse_file(ssh_session session,
const char *filename,
- int *parsing)
+ int *parsing,
+ unsigned int depth,
+ bool global)
{
FILE *f;
char line[MAX_LINE_SIZE] = {0};
unsigned int count = 0;
int rv;
+ if (depth > LIBSSH_CONF_MAX_DEPTH) {
+ ssh_set_error(session, SSH_FATAL,
+ "ERROR - Too many levels of configuration includes "
+ "when processing file '%s'", filename);
+ return;
+ }
+
f = fopen(filename, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
@@ -223,7 +225,7 @@ local_parse_file(ssh_session session,
SSH_LOG(SSH_LOG_PACKET, "Reading additional configuration data from %s", filename);
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_config_parse_line(session, line, count, parsing);
+ rv = ssh_config_parse_line(session, line, count, parsing, depth, global);
if (rv < 0) {
fclose(f);
return;
@@ -237,7 +239,9 @@ local_parse_file(ssh_session session,
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
static void local_parse_glob(ssh_session session,
const char *fileglob,
- int *parsing)
+ int *parsing,
+ unsigned int depth,
+ bool global)
{
glob_t globbuf = {
.gl_flags = 0,
@@ -257,7 +261,7 @@ static void local_parse_glob(ssh_session session,
}
for (i = 0; i < globbuf.gl_pathc; i++) {
- local_parse_file(session, globbuf.gl_pathv[i], parsing);
+ local_parse_file(session, globbuf.gl_pathv[i], parsing, depth, global);
}
globfree(&globbuf);
@@ -315,6 +319,7 @@ ssh_exec_shell(char *cmd)
char *shell = NULL;
pid_t pid;
int status, devnull, rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
shell = getenv("SHELL");
if (shell == NULL || shell[0] == '\0') {
@@ -330,7 +335,8 @@ ssh_exec_shell(char *cmd)
/* Need this to redirect subprocess stdin/out */
devnull = open("/dev/null", O_RDWR);
if (devnull == -1) {
- SSH_LOG(SSH_LOG_WARN, "Failed to open(/dev/null): %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "Failed to open(/dev/null): %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
@@ -342,13 +348,15 @@ ssh_exec_shell(char *cmd)
/* Redirect child stdin and stdout. Leave stderr */
rc = dup2(devnull, STDIN_FILENO);
if (rc == -1) {
- SSH_LOG(SSH_LOG_WARN, "dup2: %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "dup2: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
exit(1);
}
rc = dup2(devnull, STDOUT_FILENO);
if (rc == -1) {
- SSH_LOG(SSH_LOG_WARN, "dup2: %s", strerror(errno));
- exit(1);
+ SSH_LOG(SSH_LOG_WARN, "dup2: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ exit(1);
}
if (devnull > STDERR_FILENO) {
close(devnull);
@@ -362,7 +370,7 @@ ssh_exec_shell(char *cmd)
rc = execv(argv[0], argv);
if (rc == -1) {
SSH_LOG(SSH_LOG_WARN, "Failed to execute command '%s': %s", cmd,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
/* Die with signal to make this error apparent to parent. */
signal(SIGTERM, SIG_DFL);
kill(getpid(), SIGTERM);
@@ -373,19 +381,21 @@ ssh_exec_shell(char *cmd)
/* Parent */
close(devnull);
if (pid == -1) { /* Error */
- SSH_LOG(SSH_LOG_WARN, "Failed to fork child: %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "Failed to fork child: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
- SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s", strerror(errno));
+ SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
}
if (!WIFEXITED(status)) {
- SSH_LOG(SSH_LOG_WARN, "Command %s exitted abnormally", cmd);
+ SSH_LOG(SSH_LOG_WARN, "Command %s exited abnormally", cmd);
return -1;
}
SSH_LOG(SSH_LOG_TRACE, "Command '%s' returned %d", cmd, WEXITSTATUS(status));
@@ -454,7 +464,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
if (parse_entry) {
/* We actually care only about the first item */
- rv = ssh_config_parse_uri(cp, &username, &hostname, &port);
+ rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
/* The rest of the list needs to be passed on */
if (endp != NULL) {
next = strdup(endp + 1);
@@ -465,7 +475,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} else {
/* The rest is just sanity-checked to avoid failures later */
- rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
+ rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
}
if (rv != SSH_OK) {
goto out;
@@ -481,7 +491,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
if (hostname != NULL && do_parsing) {
char com[512] = {0};
- rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W [%%h]:%%p %s",
+ rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W '[%%h]:%%p' %s",
username ? " -l " : "",
username ? username : "",
port ? " -p " : "",
@@ -490,7 +500,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
next ? next : "",
hostname);
if (rv < 0 || rv >= (int)sizeof(com)) {
- SSH_LOG(SSH_LOG_WARN, "Too long ProxyJump configuration line");
+ SSH_LOG(SSH_LOG_TRACE, "Too long ProxyJump configuration line");
rv = SSH_ERROR;
goto out;
}
@@ -507,11 +517,68 @@ out:
return rv;
}
+static char *
+ssh_config_make_absolute(ssh_session session,
+ const char *path,
+ bool global)
+{
+ size_t outlen = 0;
+ char *out = NULL;
+ int rv;
+
+ /* Looks like absolute path */
+ if (path[0] == '/') {
+ return strdup(path);
+ }
+
+ /* relative path */
+ if (global) {
+ /* Parsing global config */
+ outlen = strlen(path) + strlen("/etc/ssh/") + 1;
+ out = malloc(outlen);
+ if (out == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ rv = snprintf(out, outlen, "/etc/ssh/%s", path);
+ if (rv < 1) {
+ free(out);
+ return NULL;
+ }
+ return out;
+ }
+
+ /* paths starting with tilde are already absolute */
+ if (path[0] == '~') {
+ return ssh_path_expand_tilde(path);
+ }
+
+ /* Parsing user config relative to home directory (generally ~/.ssh) */
+ if (session->opts.sshdir == NULL) {
+ ssh_set_error_invalid(session);
+ return NULL;
+ }
+ outlen = strlen(path) + strlen(session->opts.sshdir) + 1 + 1;
+ out = malloc(outlen);
+ if (out == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ rv = snprintf(out, outlen, "%s/%s", session->opts.sshdir, path);
+ if (rv < 1) {
+ free(out);
+ return NULL;
+ }
+ return out;
+}
+
static int
ssh_config_parse_line(ssh_session session,
const char *line,
unsigned int count,
- int *parsing)
+ int *parsing,
+ unsigned int depth,
+ bool global)
{
enum ssh_config_opcode_e opcode;
const char *p = NULL, *p2 = NULL;
@@ -555,7 +622,10 @@ ssh_config_parse_line(ssh_session session,
opcode != SOC_HOST &&
opcode != SOC_MATCH &&
opcode != SOC_INCLUDE &&
- opcode > SOC_UNSUPPORTED) { /* Ignore all unknown types here */
+ opcode != SOC_IDENTITY &&
+ opcode != SOC_CERTIFICATE &&
+ opcode > SOC_UNSUPPORTED &&
+ opcode < SOC_MAX) { /* Ignore all unknown types here */
/* Skip all the options that were already applied */
if (seen[opcode] != 0) {
SAFE_FREE(x);
@@ -569,11 +639,19 @@ ssh_config_parse_line(ssh_session session,
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
+ char *path = ssh_config_make_absolute(session, p, global);
+ if (path == NULL) {
+ SSH_LOG(SSH_LOG_WARN, "line %d: Failed to allocate memory "
+ "for the include path expansion", count);
+ SAFE_FREE(x);
+ return -1;
+ }
#if defined(HAVE_GLOB) && defined(HAVE_GLOB_GL_FLAGS_MEMBER)
- local_parse_glob(session, p, parsing);
+ local_parse_glob(session, path, parsing, depth + 1, global);
#else
- local_parse_file(session, p, parsing);
+ local_parse_file(session, path, parsing, depth + 1, global);
#endif /* HAVE_GLOB */
+ free(path);
}
break;
@@ -591,7 +669,7 @@ ssh_config_parse_line(ssh_session session,
break;
}
args++;
- SSH_LOG(SSH_LOG_TRACE, "line %d: Processing Match keyword '%s'",
+ SSH_LOG(SSH_LOG_DEBUG, "line %d: Processing Match keyword '%s'",
count, p);
/* If the option is prefixed with ! the result should be negated */
@@ -623,7 +701,7 @@ ssh_config_parse_line(ssh_session session,
case MATCH_FINAL:
case MATCH_CANONICAL:
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"line %d: Unsupported Match keyword '%s', skipping",
count,
p);
@@ -635,13 +713,13 @@ ssh_config_parse_line(ssh_session session,
/* Skip one argument (including in quotes) */
p = ssh_config_get_token(&s);
if (p == NULL || p[0] == '\0') {
- SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument", count, p2);
SAFE_FREE(x);
return -1;
}
if (result != 1) {
- SSH_LOG(SSH_LOG_INFO, "line %d: Skipped match exec "
+ SSH_LOG(SSH_LOG_DEBUG, "line %d: Skipped match exec "
"'%s' as previous conditions already failed.",
count, p2);
continue;
@@ -662,7 +740,7 @@ ssh_config_parse_line(ssh_session session,
}
localuser = ssh_get_local_username();
if (localuser == NULL) {
- SSH_LOG(SSH_LOG_WARN, "line %d: Can not get local username "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Can not get local username "
"for conditional matching.", count);
SAFE_FREE(x);
return -1;
@@ -676,13 +754,13 @@ ssh_config_parse_line(ssh_session session,
/* Skip one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
- SSH_LOG(SSH_LOG_WARN, "line %d: Match keyword "
+ SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument", count, p2);
SAFE_FREE(x);
return -1;
}
args++;
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_TRACE,
"line %d: Unsupported Match keyword '%s', ignoring",
count,
p2);
@@ -812,34 +890,6 @@ ssh_config_parse_line(ssh_session session,
}
}
break;
- case SOC_PROTOCOL:
- p = ssh_config_get_str_tok(&s, NULL);
- if (p && *parsing) {
- char *a, *b;
- b = strdup(p);
- if (b == NULL) {
- SAFE_FREE(x);
- ssh_set_error_oom(session);
- return -1;
- }
- i = 0;
- ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
-
- for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
- switch (atoi(a)) {
- case 1:
- break;
- case 2:
- i = 1;
- ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
- break;
- default:
- break;
- }
- }
- SAFE_FREE(b);
- }
- break;
case SOC_TIMEOUT:
l = ssh_config_get_long(&s, -1);
if (l >= 0 && *parsing) {
@@ -917,10 +967,10 @@ ssh_config_parse_line(ssh_session session,
if (strcasecmp(p, "quiet") == 0) {
value = SSH_LOG_NONE;
} else if (strcasecmp(p, "fatal") == 0 ||
- strcasecmp(p, "error")== 0 ||
- strcasecmp(p, "info") == 0) {
+ strcasecmp(p, "error")== 0) {
value = SSH_LOG_WARN;
- } else if (strcasecmp(p, "verbose") == 0) {
+ } else if (strcasecmp(p, "verbose") == 0 ||
+ strcasecmp(p, "info") == 0) {
value = SSH_LOG_INFO;
} else if (strcasecmp(p, "DEBUG") == 0 ||
strcasecmp(p, "DEBUG1") == 0) {
@@ -940,7 +990,7 @@ ssh_config_parse_line(ssh_session session,
ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
}
break;
- case SOC_PUBKEYACCEPTEDTYPES:
+ case SOC_PUBKEYACCEPTEDKEYTYPES:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
@@ -958,20 +1008,20 @@ ssh_config_parse_line(ssh_session session,
if (p == NULL) {
break;
} else if (strcmp(p, "default") == 0) {
- /* Default rekey limits enforced automaticaly */
+ /* Default rekey limits enforced automatically */
ll = 0;
} else {
char *endp = NULL;
ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) {
/* No number or negative */
- SSH_LOG(SSH_LOG_WARN, "Invalid argument to rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
break;
}
switch (*endp) {
case 'G':
if (ll > LLONG_MAX / 1024) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -979,7 +1029,7 @@ ssh_config_parse_line(ssh_session session,
FALL_THROUGH;
case 'M':
if (ll > LLONG_MAX / 1024) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -987,7 +1037,7 @@ ssh_config_parse_line(ssh_session session,
FALL_THROUGH;
case 'K':
if (ll > LLONG_MAX / 1024) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1003,7 +1053,7 @@ ssh_config_parse_line(ssh_session session,
break;
}
if (*endp != ' ' && *endp != '\0') {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid trailing characters after the rekey limit: %s",
endp);
break;
@@ -1024,14 +1074,14 @@ ssh_config_parse_line(ssh_session session,
ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) {
/* No number or negative */
- SSH_LOG(SSH_LOG_WARN, "Invalid argument to rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
break;
}
switch (*endp) {
case 'w':
case 'W':
if (ll > LLONG_MAX / 7) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1040,7 +1090,7 @@ ssh_config_parse_line(ssh_session session,
case 'd':
case 'D':
if (ll > LLONG_MAX / 24) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1049,7 +1099,7 @@ ssh_config_parse_line(ssh_session session,
case 'h':
case 'H':
if (ll > LLONG_MAX / 60) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1058,7 +1108,7 @@ ssh_config_parse_line(ssh_session session,
case 'm':
case 'M':
if (ll > LLONG_MAX / 60) {
- SSH_LOG(SSH_LOG_WARN, "Possible overflow of rekey limit");
+ SSH_LOG(SSH_LOG_TRACE, "Possible overflow of rekey limit");
ll = -1;
break;
}
@@ -1077,7 +1127,7 @@ ssh_config_parse_line(ssh_session session,
break;
}
if (*endp != '\0') {
- SSH_LOG(SSH_LOG_WARN, "Invalid trailing characters after the"
+ SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the"
" rekey limit: %s", endp);
break;
}
@@ -1113,17 +1163,68 @@ ssh_config_parse_line(ssh_session session,
}
break;
case SOC_NA:
- SSH_LOG(SSH_LOG_INFO, "Unapplicable option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d",
keyword, count);
break;
case SOC_UNSUPPORTED:
- SSH_LOG(SSH_LOG_INFO, "Unsupported option: %s, line: %d",
+ SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d",
keyword, count);
break;
case SOC_UNKNOWN:
- SSH_LOG(SSH_LOG_INFO, "Unknown option: %s, line: %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d",
keyword, count);
break;
+ case SOC_IDENTITYAGENT:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p);
+ }
+ break;
+ case SOC_IDENTITIESONLY:
+ i = ssh_config_get_yesno(&s, -1);
+ if (i >= 0 && *parsing) {
+ bool b = i;
+ ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
+ }
+ break;
+ case SOC_CONTROLMASTER:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ int value = -1;
+
+ if (strcasecmp(p, "auto") == 0) {
+ value = SSH_CONTROL_MASTER_AUTO;
+ } else if (strcasecmp(p, "yes") == 0) {
+ value = SSH_CONTROL_MASTER_YES;
+ } else if (strcasecmp(p, "no") == 0) {
+ value = SSH_CONTROL_MASTER_NO;
+ } else if (strcasecmp(p, "autoask") == 0) {
+ value = SSH_CONTROL_MASTER_AUTOASK;
+ } else if (strcasecmp(p, "ask") == 0) {
+ value = SSH_CONTROL_MASTER_ASK;
+ }
+
+ if (value != -1) {
+ ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
+ }
+ }
+ break;
+ case SOC_CONTROLPATH:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p == NULL) {
+ SAFE_FREE(x);
+ return -1;
+ }
+ if (*parsing) {
+ ssh_options_set(session, SSH_OPTIONS_CONTROL_PATH, p);
+ }
+ break;
+ case SOC_CERTIFICATE:
+ p = ssh_config_get_str_tok(&s, NULL);
+ if (p && *parsing) {
+ ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
+ }
+ break;
default:
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d",
opcode);
@@ -1149,18 +1250,24 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
unsigned int count = 0;
FILE *f;
int parsing, rv;
+ bool global = 0;
f = fopen(filename, "r");
if (f == NULL) {
return 0;
}
+ rv = strcmp(filename, GLOBAL_CLIENT_CONFIG);
+ if (rv == 0) {
+ global = true;
+ }
+
SSH_LOG(SSH_LOG_PACKET, "Reading configuration data from %s", filename);
parsing = 1;
while (fgets(line, sizeof(line), f)) {
count++;
- rv = ssh_config_parse_line(session, line, count, &parsing);
+ rv = ssh_config_parse_line(session, line, count, &parsing, 0, global);
if (rv < 0) {
fclose(f);
return -1;
@@ -1170,3 +1277,57 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
fclose(f);
return 0;
}
+
+/* @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
+ *
+ * @returns SSH_OK on successful parsing the configuration string,
+ * SSH_ERROR on error
+ */
+int ssh_config_parse_string(ssh_session session, const char *input)
+{
+ char line[MAX_LINE_SIZE] = {0};
+ const char *c = input, *line_start = input;
+ unsigned int line_num = 0, line_len;
+ int parsing, rv;
+
+ SSH_LOG(SSH_LOG_DEBUG, "Reading configuration data from string:");
+ SSH_LOG(SSH_LOG_DEBUG, "START\n%s\nEND", input);
+
+ parsing = 1;
+ while (1) {
+ line_num++;
+ line_start = c;
+ c = strchr(line_start, '\n');
+ if (c == NULL) {
+ /* if there is no newline at the end of the string */
+ c = strchr(line_start, '\0');
+ }
+ if (c == NULL) {
+ /* should not happen, would mean a string without trailing '\0' */
+ SSH_LOG(SSH_LOG_TRACE, "No trailing '\\0' in config string");
+ return SSH_ERROR;
+ }
+ line_len = c - line_start;
+ if (line_len > MAX_LINE_SIZE - 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Line %u too long: %u characters",
+ line_num, line_len);
+ return SSH_ERROR;
+ }
+ memcpy(line, line_start, line_len);
+ line[line_len] = '\0';
+ SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
+ rv = ssh_config_parse_line(session, line, line_num, &parsing, 0, false);
+ if (rv < 0) {
+ return SSH_ERROR;
+ }
+ if (*c == '\0') {
+ break;
+ }
+ c++;
+ }
+
+ return SSH_OK;
+}
diff --git a/src/config_parser.c b/src/config_parser.c
index 2f91d39f..bd6ab9d7 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -30,9 +30,10 @@
#include "libssh/config_parser.h"
#include "libssh/priv.h"
+#include "libssh/misc.h"
/* Returns the original string after skipping the leading whitespace
- * and optional quotes.
+ * until finding LF.
* This is useful in case we need to get the rest of the line (for example
* external command).
*/
@@ -48,15 +49,6 @@ char *ssh_config_get_cmd(char **str)
}
}
- if (*c == '\"') {
- for (r = ++c; *c; c++) {
- if (*c == '\"') {
- *c = '\0';
- goto out;
- }
- }
- }
-
for (r = c; *c; c++) {
if (*c == '\n') {
*c = '\0';
@@ -170,12 +162,14 @@ int ssh_config_get_yesno(char **str, int notfound)
}
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port)
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port)
{
char *endp = NULL;
long port_n;
+ int rc;
/* Sanitize inputs */
if (username != NULL) {
@@ -189,7 +183,7 @@ int ssh_config_parse_uri(const char *tok,
}
/* Username part (optional) */
- endp = strchr(tok, '@');
+ endp = strrchr(tok, '@');
if (endp != NULL) {
/* Zero-length username is not valid */
if (tok == endp) {
@@ -200,6 +194,10 @@ int ssh_config_parse_uri(const char *tok,
if (*username == NULL) {
goto error;
}
+ rc = ssh_check_username_syntax(*username);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
tok = endp + 1;
/* If there is second @ character, this does not look like our URI */
@@ -217,12 +215,17 @@ int ssh_config_parse_uri(const char *tok,
if (endp == NULL) {
goto error;
}
- } else {
- /* Hostnames or aliases expand to the last colon or to the end */
+ } else if (!ignore_port) {
+ /* Hostnames or aliases expand to the last colon (if port is requested)
+ * or to the end */
endp = strrchr(tok, ':');
if (endp == NULL) {
endp = strchr(tok, '\0');
}
+ } else {
+ /* If no port is requested, expand to the end of line
+ * (to accommodate the IPv6 addresses) */
+ endp = strchr(tok, '\0');
}
if (tok == endp) {
/* Zero-length hostnames are not valid */
@@ -233,6 +236,14 @@ int ssh_config_parse_uri(const char *tok,
if (*hostname == NULL) {
goto error;
}
+ /* if not an ip, check syntax */
+ rc = ssh_is_ipaddr(*hostname);
+ if (rc == 0) {
+ rc = ssh_check_hostname_syntax(*hostname);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
}
/* Skip also the closing bracket */
if (*endp == ']') {
@@ -246,7 +257,7 @@ int ssh_config_parse_uri(const char *tok,
/* Verify the port is valid positive number */
port_n = strtol(endp + 1, &port_end, 10);
if (port_n < 1 || *port_end != '\0') {
- SSH_LOG(SSH_LOG_WARN, "Failed to parse port number."
+ SSH_LOG(SSH_LOG_TRACE, "Failed to parse port number."
" The value '%ld' is invalid or there are some"
" trailing characters: '%s'", port_n, port_end);
goto error;
diff --git a/src/connect.c b/src/connect.c
index 252e2c63..dd3bbcf5 100644
--- a/src/connect.c
+++ b/src/connect.c
@@ -29,6 +29,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
#include "libssh/libssh.h"
#include "libssh/misc.h"
@@ -51,11 +54,6 @@
#define NTDDI_VERSION 0x05010000 /* NTDDI_WINXP */
#endif
-#if _MSC_VER >= 1400
-#include <io.h>
-#undef close
-#define close _close
-#endif /* _MSC_VER */
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -133,7 +131,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
#endif
}
- if (ssh_is_ipaddr(host)) {
+ if (ssh_is_ipaddr(host) == 1) {
/* this is an IP address */
SSH_LOG(SSH_LOG_PACKET, "host %s matches an IP address", host);
hints.ai_flags |= AI_NUMERICHOST;
@@ -165,7 +163,7 @@ static int set_tcp_nodelay(socket_t socket)
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
const char *bind_addr, int port)
{
- socket_t s = -1;
+ socket_t s = -1, first = -1;
int rc;
struct addrinfo *ai = NULL;
struct addrinfo *itr = NULL;
@@ -180,11 +178,13 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
}
for (itr = ai; itr != NULL; itr = itr->ai_next) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
/* create socket */
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
if (s < 0) {
ssh_set_error(session, SSH_FATAL,
- "Socket create failed: %s", strerror(errno));
+ "Socket create failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
continue;
}
@@ -211,7 +211,8 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
{
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
ssh_set_error(session, SSH_FATAL,
- "Binding local address: %s", strerror(errno));
+ "Binding local address: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
continue;
} else {
break;
@@ -243,7 +244,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to set TCP_NODELAY on socket: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
ssh_connect_socket_close(s);
s = -1;
continue;
@@ -252,11 +253,22 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
errno = 0;
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
- if (rc == -1 && (errno != 0) && (errno != EINPROGRESS)) {
- ssh_set_error(session, SSH_FATAL,
- "Failed to connect: %s", strerror(errno));
- ssh_connect_socket_close(s);
- s = -1;
+ if (rc == -1) {
+ if ((errno != 0) && (errno != EINPROGRESS)) {
+ ssh_set_error(session, SSH_FATAL,
+ "Failed to connect: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ ssh_connect_socket_close(s);
+ s = -1;
+ } else {
+ if (first == -1) {
+ first = s;
+ } else { /* errno == EINPROGRESS */
+ /* save only the first "working" socket */
+ ssh_connect_socket_close(s);
+ s = -1;
+ }
+ }
continue;
}
@@ -265,6 +277,12 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
freeaddrinfo(ai);
+ /* first let's go through all the addresses looking for immediate
+ * connection, otherwise return the first address without error or error */
+ if (s == -1) {
+ s = first;
+ }
+
return s;
}
@@ -286,14 +304,14 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata)
/**
* @brief A wrapper for the select syscall
*
- * This functions acts more or less like the select(2) syscall.\n
+ * This function acts more or less like the select(2) syscall.\n
* There is no support for writing or exceptions.\n
*
* @param[in] channels Arrays of channels pointers terminated by a NULL.
* It is never rewritten.
*
- * @param[out] outchannels Arrays of same size that "channels", there is no need
- * to initialize it.
+ * @param[out] outchannels Arrays of the same size as "channels", there is no
+ * need to initialize it.
*
* @param[in] maxfd Maximum +1 file descriptor from readfds.
*
diff --git a/src/connector.c b/src/connector.c
index ac841338..21c4d79b 100644
--- a/src/connector.c
+++ b/src/connector.c
@@ -30,21 +30,11 @@
#include <stdbool.h>
#include <sys/stat.h>
+#ifndef CHUNKSIZE
#define CHUNKSIZE 4096
+#endif
-#ifdef _WIN32
-# ifdef HAVE_IO_H
-# include <io.h>
-# undef open
-# define open _open
-# undef close
-# define close _close
-# undef read
-# define read _read
-# undef unlink
-# define unlink _unlink
-# endif /* HAVE_IO_H */
-#else
+#ifndef _WIN32
# include <sys/types.h>
# include <sys/socket.h>
#endif
@@ -83,7 +73,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
void *userdata);
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
- size_t bytes,
+ uint32_t bytes,
void *userdata);
static ssize_t ssh_connector_fd_read(ssh_connector connector,
void *buffer,
@@ -214,7 +204,7 @@ static void ssh_connector_except_channel(ssh_connector connector,
/**
* @internal
*
- * @brief Reset the poll events to be followed for each file descriptors.
+ * @brief Reset the poll events to be followed for each file descriptor.
*/
static void ssh_connector_reset_pollevents(ssh_connector connector)
{
@@ -246,14 +236,14 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
uint32_t toread = CHUNKSIZE;
ssize_t r;
ssize_t w;
- int total = 0;
+ ssize_t total = 0;
int rc;
SSH_LOG(SSH_LOG_TRACE, "connector POLLIN event for fd %d", connector->in_fd);
if (connector->out_wontblock) {
if (connector->out_channel != NULL) {
- size_t size = ssh_channel_window_size(connector->out_channel);
+ uint32_t size = ssh_channel_window_size(connector->out_channel);
/* Don't attempt reading more than the window */
toread = MIN(size, CHUNKSIZE);
@@ -324,35 +314,40 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
/** @internal
* @brief Callback called when a poll event is received on an output fd
*/
-static void ssh_connector_fd_out_cb(ssh_connector connector){
+static void
+ssh_connector_fd_out_cb(ssh_connector connector)
+{
unsigned char buffer[CHUNKSIZE];
- int r;
- int w;
- int total = 0;
- SSH_LOG(SSH_LOG_TRACE, "connector POLLOUT event for fd %d", connector->out_fd);
+ ssize_t r;
+ ssize_t w;
+ ssize_t total = 0;
+ SSH_LOG(SSH_LOG_TRACE, "connector POLLOUT event for fd %d",
+ connector->out_fd);
- if(connector->in_available){
- if (connector->in_channel != NULL){
- r = ssh_channel_read_nonblocking(connector->in_channel, buffer, CHUNKSIZE, 0);
- if(r == SSH_ERROR){
+ if (connector->in_available) {
+ if (connector->in_channel != NULL) {
+ r = ssh_channel_read_nonblocking(connector->in_channel, buffer,
+ CHUNKSIZE, 0);
+ if (r == SSH_ERROR) {
ssh_connector_except_channel(connector, connector->in_channel);
return;
- } else if(r == 0 && ssh_channel_is_eof(connector->in_channel)){
+ } else if (r == 0 && ssh_channel_is_eof(connector->in_channel)) {
close(connector->out_fd);
connector->out_fd = SSH_INVALID_SOCKET;
return;
- } else if(r>0) {
+ } else if (r > 0) {
/* loop around write in case the write blocks even for CHUNKSIZE bytes */
- while (total != r){
- w = ssh_connector_fd_write(connector, buffer + total, r - total);
- if (w < 0){
+ while (total != r) {
+ w = ssh_connector_fd_write(connector, buffer + total,
+ r - total);
+ if (w < 0) {
ssh_connector_except(connector, connector->out_fd);
return;
}
total += w;
}
}
- } else if (connector->in_fd != SSH_INVALID_SOCKET){
+ } else if (connector->in_fd != SSH_INVALID_SOCKET) {
/* fallback on the socket input callback */
connector->out_wontblock = 1;
ssh_connector_fd_in_cb(connector);
@@ -430,7 +425,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
{
ssh_connector connector = userdata;
int w;
- size_t window;
+ uint32_t window;
(void) session;
(void) channel;
@@ -444,11 +439,14 @@ static int ssh_connector_channel_data_cb(ssh_session session,
} else if (!is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDOUT)) {
/* ignore stdout */
return 0;
+ } else if (len == 0) {
+ /* ignore empty data */
+ return 0;
}
if (connector->out_wontblock) {
if (connector->out_channel != NULL) {
- int window_len;
+ uint32_t window_len;
window = ssh_channel_window_size(connector->out_channel);
window_len = MIN(window, len);
@@ -512,7 +510,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
*/
static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
ssh_channel channel,
- size_t bytes,
+ uint32_t bytes,
void *userdata)
{
ssh_connector connector = userdata;
@@ -524,7 +522,7 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
SSH_LOG(SSH_LOG_TRACE, "Channel write won't block");
if (connector->in_available) {
if (connector->in_channel != NULL) {
- size_t len = MIN(CHUNKSIZE, bytes);
+ uint32_t len = MIN(CHUNKSIZE, bytes);
r = ssh_channel_read_nonblocking(connector->in_channel,
buffer,
diff --git a/src/crypto_common.c b/src/crypto_common.c
new file mode 100644
index 00000000..5dc883f6
--- /dev/null
+++ b/src/crypto_common.c
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2020 by Anderson Toshiyuki Sasaki - Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libssh/crypto.h"
+
+int secure_memcmp(const void *s1, const void *s2, size_t n)
+{
+ size_t i;
+ uint8_t status = 0;
+ const uint8_t *p1 = s1;
+ const uint8_t *p2 = s2;
+
+ for (i = 0; i < n; i++) {
+ status |= (p1[i] ^ p2[i]);
+ }
+
+ return (status != 0);
+}
diff --git a/src/curve25519.c b/src/curve25519.c
index c13b3604..3f57f25d 100644
--- a/src/curve25519.c
+++ b/src/curve25519.c
@@ -39,7 +39,7 @@
#include "libssh/pki.h"
#include "libssh/bignum.h"
-#ifdef HAVE_OPENSSL_X25519
+#ifdef HAVE_LIBCRYPTO
#include <openssl/err.h>
#endif
@@ -59,7 +59,7 @@ static struct ssh_packet_callbacks_struct ssh_curve25519_client_callbacks = {
static int ssh_curve25519_init(ssh_session session)
{
int rc;
-#ifdef HAVE_OPENSSL_X25519
+#ifdef HAVE_LIBCRYPTO
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL;
size_t pubkey_len = CURVE25519_PUBKEY_SIZE;
@@ -136,7 +136,7 @@ static int ssh_curve25519_init(ssh_session session)
crypto_scalarmult_base(session->next_crypto->curve25519_client_pubkey,
session->next_crypto->curve25519_privkey);
}
-#endif /* HAVE_OPENSSL_X25519 */
+#endif /* HAVE_LIBCRYPTO */
return SSH_OK;
}
@@ -172,11 +172,16 @@ int ssh_client_curve25519_init(ssh_session session)
return rc;
}
+void ssh_client_curve25519_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
+}
+
static int ssh_curve25519_build_k(ssh_session session)
{
ssh_curve25519_pubkey k;
-#ifdef HAVE_OPENSSL_X25519
+#ifdef HAVE_LIBCRYPTO
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *pkey = NULL, *pubkey = NULL;
size_t shared_key_len = sizeof(k);
@@ -255,7 +260,7 @@ out:
crypto_scalarmult(k, session->next_crypto->curve25519_privkey,
session->next_crypto->curve25519_server_pubkey);
}
-#endif /* HAVE_OPENSSL_X25519 */
+#endif /* HAVE_LIBCRYPTO */
bignum_bin2bn(k, CURVE25519_PUBKEY_SIZE, &session->next_crypto->shared_secret);
if (session->next_crypto->shared_secret == NULL) {
@@ -285,7 +290,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
+ ssh_client_curve25519_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) {
@@ -330,16 +335,10 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_curve25519_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
@@ -377,12 +376,12 @@ void ssh_server_curve25519_init(ssh_session session){
*/
static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
/* ECDH keys */
- ssh_string q_c_string;
- ssh_string q_s_string;
+ ssh_string q_c_string = NULL;
+ ssh_string q_s_string = NULL;
ssh_string server_pubkey_blob = NULL;
- /* SSH host keys (rsa,dsa,ecdsa) */
- ssh_key privkey;
+ /* SSH host keys (rsa, ed25519 and ecdsa) */
+ ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
int rc;
@@ -402,15 +401,14 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
SSH_FATAL,
"Incorrect size for server Curve25519 public key: %zu",
ssh_string_len(q_c_string));
- SSH_STRING_FREE(q_c_string);
goto error;
}
memcpy(session->next_crypto->curve25519_client_pubkey,
ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE);
SSH_STRING_FREE(q_c_string);
- /* Build server's keypair */
+ /* Build server's key pair */
rc = ssh_curve25519_init(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Failed to generate curve25519 keys");
@@ -460,12 +458,17 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
/* add ecdh public key */
q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE);
if (q_s_string == NULL) {
+ ssh_set_error_oom(session);
goto error;
}
- ssh_string_fill(q_s_string,
- session->next_crypto->curve25519_server_pubkey,
- CURVE25519_PUBKEY_SIZE);
+ rc = ssh_string_fill(q_s_string,
+ session->next_crypto->curve25519_server_pubkey,
+ CURVE25519_PUBKEY_SIZE);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL, "Could not copy public key");
+ goto error;
+ }
rc = ssh_buffer_add_ssh_string(session->out_buffer, q_s_string);
SSH_STRING_FREE(q_s_string);
@@ -487,27 +490,24 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_curve25519_init){
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_ECDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_ECDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
return SSH_ERROR;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
+ SSH_STRING_FREE(q_c_string);
+ SSH_STRING_FREE(q_s_string);
ssh_buffer_reinit(session->out_buffer);
session->session_state=SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
diff --git a/src/dh-gex.c b/src/dh-gex.c
index 9bf0546a..4e59073f 100644
--- a/src/dh-gex.c
+++ b/src/dh-gex.c
@@ -37,7 +37,7 @@
#include "libssh/buffer.h"
#include "libssh/session.h"
-/* Minimum, recommanded and maximum size of DH group */
+/* Minimum, recommended and maximum size of DH group */
#define DH_PMIN 2048
#define DH_PREQ 2048
#define DH_PMAX 8192
@@ -108,11 +108,15 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
bignum pmin1 = NULL, one = NULL;
bignum_CTX ctx = bignum_ctx_new();
bignum modulus = NULL, generator = NULL;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pubkey;
+#else
+ bignum pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
(void) type;
(void) user;
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_GROUP received");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_DH_GEX_GROUP received");
if (bignum_ctx_invalid(ctx)) {
goto error;
@@ -212,6 +216,9 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group)
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
session->dh_handshake_state = DH_STATE_INIT_SENT;
@@ -229,6 +236,9 @@ error:
bignum_safe_free(generator);
bignum_safe_free(one);
bignum_safe_free(pmin1);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
if(!bignum_ctx_invalid(ctx)) {
bignum_ctx_free(ctx);
}
@@ -238,6 +248,11 @@ error:
return SSH_PACKET_USED;
}
+void ssh_client_dhgex_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+}
+
static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
{
struct ssh_crypto_struct *crypto=session->next_crypto;
@@ -246,9 +261,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
bignum server_pubkey = NULL;
(void)type;
(void)user;
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_REPLY received");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEX_DH_GEX_REPLY received");
- ssh_packet_remove_callbacks(session, &ssh_dhgex_client_callbacks);
+ ssh_client_dhgex_remove_callbacks(session);
rc = ssh_buffer_unpack(packet,
"SBS",
&pubkey_blob, &server_pubkey,
@@ -263,6 +278,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
bignum_safe_free(server_pubkey);
goto error;
}
+ /* The ownership was passed to the crypto structure */
+ server_pubkey = NULL;
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
SSH_STRING_FREE(pubkey_blob);
@@ -280,19 +297,15 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply)
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc = ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
+ SSH_STRING_FREE(pubkey_blob);
ssh_dh_cleanup(session->next_crypto);
session->session_state = SSH_SESSION_STATE_ERROR;
@@ -364,9 +377,9 @@ static bool dhgroup_better_size(uint32_t pmin,
* @brief returns 1 with 1/n probability
* @returns 1 on with P(1/n), 0 with P(n-1/n).
*/
-static bool invn_chance(int n)
+static bool invn_chance(size_t n)
{
- uint32_t nounce = 0;
+ size_t nounce = 0;
int ok;
ok = ssh_get_random(&nounce, sizeof(nounce), 0);
@@ -422,7 +435,7 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
if (rc == EOF) {
break;
}
- SSH_LOG(SSH_LOG_INFO, "Invalid moduli entry line %zu", line);
+ SSH_LOG(SSH_LOG_DEBUG, "Invalid moduli entry line %zu", line);
do {
firstbyte = getc(moduli);
} while(firstbyte != '\n' && firstbyte != EOF);
@@ -460,14 +473,14 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
}
}
if (*best_size != 0) {
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Selected %zu bits modulus out of %zu candidates in %zu lines",
*best_size,
best_nlines - 1,
line);
} else {
- SSH_LOG(SSH_LOG_WARNING,
- "No moduli found for [%u:%u:%u]",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "No moduli found for [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
pmin,
pn,
pmax);
@@ -486,7 +499,8 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
* @param[out] g generator
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
-static int ssh_retrieve_dhgroup(uint32_t pmin,
+static int ssh_retrieve_dhgroup(char *moduli_file,
+ uint32_t pmin,
uint32_t pn,
uint32_t pmax,
size_t *size,
@@ -505,12 +519,17 @@ static int ssh_retrieve_dhgroup(uint32_t pmin,
return ssh_fallback_group(pmax, p, g);
}
- moduli = fopen(MODULI_FILE, "r");
+ if (moduli_file != NULL)
+ moduli = fopen(moduli_file, "r");
+ else
+ moduli = fopen(MODULI_FILE, "r");
+
if (moduli == NULL) {
- SSH_LOG(SSH_LOG_WARNING,
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ SSH_LOG(SSH_LOG_DEBUG,
"Unable to open moduli file: %s",
- strerror(errno));
- return ssh_fallback_group(pmax, p, g);
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ return ssh_fallback_group(pmax, p, g);
}
*size = 0;
@@ -602,12 +621,12 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
ssh_set_error_invalid(session);
goto error;
}
- SSH_LOG(SSH_LOG_INFO, "dh-gex: DHGEX_REQUEST[%u:%u:%u]", pmin, pn, pmax);
+ SSH_LOG(SSH_LOG_DEBUG, "dh-gex: DHGEX_REQUEST[%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]", pmin, pn, pmax);
if (pmin > pn || pn > pmax || pn > DH_PMAX || pmax < DH_PMIN) {
ssh_set_error(session,
SSH_FATAL,
- "Invalid dh-gex arguments [%u:%u:%u]",
+ "Invalid dh-gex arguments [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
pmin,
pn,
pmax);
@@ -624,7 +643,8 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
pn = pmin;
}
}
- rc = ssh_retrieve_dhgroup(pmin,
+ rc = ssh_retrieve_dhgroup(session->opts.moduli_file,
+ pmin,
pn,
pmax,
&size,
@@ -633,7 +653,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request)
if (rc == SSH_ERROR) {
ssh_set_error(session,
SSH_FATAL,
- "Couldn't find DH group for [%u:%u:%u]",
+ "Couldn't find DH group for [%" PRIu32 ":%" PRIu32 ":%" PRIu32 "]",
pmin,
pn,
pmax);
diff --git a/src/dh.c b/src/dh.c
index 05903daf..e19e43d1 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -25,6 +25,8 @@
#include "config.h"
+#include <stdio.h>
+
#include "libssh/priv.h"
#include "libssh/crypto.h"
#include "libssh/buffer.h"
@@ -309,7 +311,11 @@ static struct ssh_packet_callbacks_struct ssh_dh_client_callbacks = {
*/
int ssh_client_dh_init(ssh_session session){
struct ssh_crypto_struct *crypto = session->next_crypto;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pubkey;
+#else
+ bignum pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
int rc;
rc = ssh_dh_init_common(crypto);
@@ -330,6 +336,9 @@ int ssh_client_dh_init(ssh_session session){
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_dh_client_callbacks);
@@ -338,10 +347,18 @@ int ssh_client_dh_init(ssh_session session){
rc = ssh_packet_send(session);
return rc;
error:
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(pubkey);
+#endif
ssh_dh_cleanup(crypto);
return SSH_ERROR;
}
+void ssh_client_dh_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_dh_client_callbacks);
+}
+
SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
struct ssh_crypto_struct *crypto=session->next_crypto;
ssh_string pubkey_blob = NULL;
@@ -351,7 +368,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_dh_client_callbacks);
+ ssh_client_dh_remove_callbacks(session);
rc = ssh_buffer_unpack(packet, "SBS", &pubkey_blob, &server_pubkey,
&crypto->dh_server_signature);
@@ -361,6 +378,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, DH_SERVER_KEYPAIR,
NULL, server_pubkey);
if (rc != SSH_OK) {
+ SSH_STRING_FREE(pubkey_blob);
bignum_safe_free(server_pubkey);
goto error;
}
@@ -369,7 +387,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
if (rc != 0) {
goto error;
}
-
+
rc = ssh_dh_compute_shared_secret(session->next_crypto->dh_ctx,
DH_CLIENT_KEYPAIR, DH_SERVER_KEYPAIR,
&session->next_crypto->shared_secret);
@@ -380,16 +398,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
@@ -435,7 +447,11 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
bignum client_pubkey;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum server_pubkey;
+#else
+ bignum server_pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
int packet_type;
int rc;
@@ -515,6 +531,9 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
sig_blob);
SSH_STRING_FREE(sig_blob);
SSH_STRING_FREE(pubkey_blob);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(server_pubkey);
+#endif
if(rc != SSH_OK) {
ssh_set_error_oom(session);
ssh_buffer_reinit(session->out_buffer);
@@ -526,20 +545,20 @@ int ssh_server_dh_process_init(ssh_session session, ssh_buffer packet)
}
SSH_LOG(SSH_LOG_DEBUG, "Sent KEX_DH_[GEX]_REPLY");
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- ssh_buffer_reinit(session->out_buffer);
- goto error;
- }
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
- if (ssh_packet_send(session) == SSH_ERROR) {
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent");
return SSH_OK;
error:
SSH_STRING_FREE(sig_blob);
SSH_STRING_FREE(pubkey_blob);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(server_pubkey);
+#endif
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_dh_cleanup(session->next_crypto);
@@ -639,7 +658,7 @@ ssh_key ssh_dh_get_current_server_publickey(ssh_session session)
return session->current_crypto->server_pubkey;
}
-/* Caller need to free the blob */
+/* Caller needs to free the blob */
int ssh_dh_get_current_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob)
{
@@ -653,7 +672,7 @@ ssh_key ssh_dh_get_next_server_publickey(ssh_session session)
return session->next_crypto->server_pubkey;
}
-/* Caller need to free the blob */
+/* Caller needs to free the blob */
int ssh_dh_get_next_server_publickey_blob(ssh_session session,
ssh_string *pubkey_blob)
{
@@ -682,7 +701,7 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
char *b64_unpadded = NULL;
size_t k;
- b64_padded = (char *)bin_to_base64(hash, (int)len);
+ b64_padded = (char *)bin_to_base64(hash, len);
if (b64_padded == NULL) {
return NULL;
}
@@ -698,7 +717,7 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
* @brief Get a hash as a human-readable hex- or base64-string.
*
* This gets an allocated fingerprint hash. If it is a SHA sum, it will
- * return an unpadded base64 strings. If it is a MD5 sum, it will return hex
+ * return an unpadded base64 string. If it is a MD5 sum, it will return a hex
* string. Either way, the output is prepended by the hash-type.
*
* @warning Do NOT use MD5 or SHA1! Those hash functions are being deprecated.
@@ -710,7 +729,8 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
*
* @param len Length of the buffer to convert.
*
- * @return Returns the allocated fingerprint hash or NULL on error.
+ * @return Returns the allocated fingerprint hash or NULL on error. The caller
+ * needs to free the memory using ssh_string_free_char().
*
* @see ssh_string_free_char()
*/
diff --git a/src/dh_crypto.c b/src/dh_crypto.c
index 3b3495c1..9ff7ad3c 100644
--- a/src/dh_crypto.c
+++ b/src/dh_crypto.c
@@ -30,6 +30,13 @@
#include "openssl/crypto.h"
#include "openssl/dh.h"
#include "libcrypto-compat.h"
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/evp.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <openssl/err.h>
+#endif /* OPENSSL_VERSION_NUMBER */
extern bignum ssh_dh_generator;
extern bignum ssh_dh_group1;
@@ -38,13 +45,21 @@ extern bignum ssh_dh_group16;
extern bignum ssh_dh_group18;
struct dh_ctx {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
DH *keypair[2];
+#else
+ EVP_PKEY *keypair[2];
+#endif /* OPENSSL_VERSION_NUMBER */
};
void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
{
#ifdef DEBUG_CRYPTO
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum x = NULL, y = NULL, e = NULL, f = NULL;
+#else
+ bignum x = NULL, y = NULL, e = NULL, f = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
ssh_dh_keypair_get_keys(c->dh_ctx, DH_CLIENT_KEYPAIR, &x, &e);
ssh_dh_keypair_get_keys(c->dh_ctx, DH_SERVER_KEYPAIR, &y, &f);
@@ -52,6 +67,12 @@ void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
ssh_print_bignum("y", y);
ssh_print_bignum("e", e);
ssh_print_bignum("f", f);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(x);
+ bignum_safe_free(y);
+ bignum_safe_free(e);
+ bignum_safe_free(f);
+#endif /* OPENSSL_VERSION_NUMBER */
ssh_log_hexdump("Session server cookie", c->server_kex.cookie, 16);
ssh_log_hexdump("Session client cookie", c->client_kex.cookie, 16);
@@ -59,9 +80,10 @@ void ssh_dh_debug_crypto(struct ssh_crypto_struct *c)
#else
(void)c; /* UNUSED_PARAM */
-#endif
+#endif /* DEBUG_CRYPTO */
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
const_bignum *priv, const_bignum *pub)
{
@@ -70,22 +92,76 @@ int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
(ctx->keypair[peer] == NULL)) {
return SSH_ERROR;
}
+
DH_get0_key(ctx->keypair[peer], pub, priv);
+
+ if (priv && (*priv == NULL || bignum_num_bits(*priv) == 0)) {
+ return SSH_ERROR;
+ }
+ if (pub && (*pub == NULL || bignum_num_bits(*pub) == 0)) {
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
+#else
+/* If set *priv and *pub should be initialized
+ * to NULL before calling this function*/
+int ssh_dh_keypair_get_keys(struct dh_ctx *ctx, int peer,
+ bignum *priv, bignum *pub)
+{
+ int rc;
+ if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) ||
+ ((priv == NULL) && (pub == NULL)) || (ctx == NULL) ||
+ (ctx->keypair[peer] == NULL)) {
+ return SSH_ERROR;
+ }
+
+ if (priv) {
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[peer],
+ OSSL_PKEY_PARAM_PRIV_KEY,
+ priv);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ }
+ if (pub) {
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[peer],
+ OSSL_PKEY_PARAM_PUB_KEY,
+ pub);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ }
if (priv && (*priv == NULL || bignum_num_bits(*priv) == 0)) {
+ if (pub && (*pub != NULL && bignum_num_bits(*pub) != 0)) {
+ bignum_safe_free(*pub);
+ *pub = NULL;
+ }
return SSH_ERROR;
}
if (pub && (*pub == NULL || bignum_num_bits(*pub) == 0)) {
+ if (priv) {
+ bignum_safe_free(*priv);
+ *priv = NULL;
+ }
return SSH_ERROR;
}
return SSH_OK;
}
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
- const bignum priv, const bignum pub)
+ bignum priv, bignum pub)
{
- bignum priv_key = NULL;
- bignum pub_key = NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ int rc;
+ OSSL_PARAM *params = NULL, *out_params = NULL, *merged_params = NULL;
+ OSSL_PARAM_BLD *param_bld = NULL;
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
if (((peer != DH_CLIENT_KEYPAIR) && (peer != DH_SERVER_KEYPAIR)) ||
((priv == NULL) && (pub == NULL)) || (ctx == NULL) ||
@@ -93,17 +169,85 @@ int ssh_dh_keypair_set_keys(struct dh_ctx *ctx, int peer,
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ (void)DH_set0_key(ctx->keypair[peer], pub, priv);
+
+ return SSH_OK;
+#else
+ rc = EVP_PKEY_todata(ctx->keypair[peer], EVP_PKEY_KEYPAIR, &out_params);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+
+ param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ evp_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ctx->keypair[peer], NULL);
+ if (evp_ctx == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = EVP_PKEY_fromdata_init(evp_ctx);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
if (priv) {
- priv_key = priv;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, priv);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
}
if (pub) {
- pub_key = pub;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PUB_KEY, pub);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
}
- (void)DH_set0_key(ctx->keypair[peer], pub_key, priv_key);
- return SSH_OK;
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+ OSSL_PARAM_BLD_free(param_bld);
+
+ merged_params = OSSL_PARAM_merge(out_params, params);
+ if (merged_params == NULL) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = EVP_PKEY_fromdata(evp_ctx,
+ &(ctx->keypair[peer]),
+ EVP_PKEY_PUBLIC_KEY,
+ merged_params);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto out;
+ }
+
+ rc = SSH_OK;
+out:
+ bignum_safe_free(priv);
+ bignum_safe_free(pub);
+ EVP_PKEY_CTX_free(evp_ctx);
+ OSSL_PARAM_free(out_params);
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_free(merged_params);
+
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
int ssh_dh_get_parameters(struct dh_ctx *ctx,
const_bignum *modulus, const_bignum *generator)
{
@@ -113,18 +257,51 @@ int ssh_dh_get_parameters(struct dh_ctx *ctx,
DH_get0_pqg(ctx->keypair[0], modulus, NULL, generator);
return SSH_OK;
}
+#else
+int ssh_dh_get_parameters(struct dh_ctx *ctx,
+ bignum *modulus, bignum *generator)
+{
+ int rc;
+
+ if (ctx == NULL || ctx->keypair[0] == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[0], OSSL_PKEY_PARAM_FFC_P, (BIGNUM**)modulus);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ rc = EVP_PKEY_get_bn_param(ctx->keypair[0], OSSL_PKEY_PARAM_FFC_G, (BIGNUM**)generator);
+ if (rc != 1) {
+ bignum_safe_free(*modulus);
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_dh_set_parameters(struct dh_ctx *ctx,
const bignum modulus, const bignum generator)
{
size_t i;
int rc;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM *params = NULL;
+ OSSL_PARAM_BLD *param_bld = NULL;
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
if ((ctx == NULL) || (modulus == NULL) || (generator == NULL)) {
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ evp_ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL);
+#endif
+
for (i = 0; i < 2; i++) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
bignum p = NULL;
bignum g = NULL;
@@ -146,16 +323,78 @@ int ssh_dh_set_parameters(struct dh_ctx *ctx,
rc = SSH_ERROR;
goto done;
}
+#else
+ param_bld = OSSL_PARAM_BLD_new();
+
+ if (param_bld == NULL) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, modulus);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, generator);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ OSSL_PARAM_BLD_free(param_bld);
+ rc = SSH_ERROR;
+ goto done;
+ }
+ OSSL_PARAM_BLD_free(param_bld);
+
+ rc = EVP_PKEY_fromdata_init(evp_ctx);
+ if (rc != 1) {
+ OSSL_PARAM_free(params);
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ /* make sure to invalidate existing keys */
+ EVP_PKEY_free(ctx->keypair[i]);
+ ctx->keypair[i] = NULL;
+
+ rc = EVP_PKEY_fromdata(evp_ctx,
+ &(ctx->keypair[i]),
+ EVP_PKEY_KEY_PARAMETERS,
+ params);
+ if (rc != 1) {
+ OSSL_PARAM_free(params);
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
}
rc = SSH_OK;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
done:
if (rc != SSH_OK) {
DH_free(ctx->keypair[0]);
DH_free(ctx->keypair[1]);
+ }
+#else
+done:
+ EVP_PKEY_CTX_free(evp_ctx);
+
+ if (rc != SSH_OK) {
+ EVP_PKEY_free(ctx->keypair[0]);
+ EVP_PKEY_free(ctx->keypair[1]);
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (rc != SSH_OK) {
ctx->keypair[0] = NULL;
ctx->keypair[1] = NULL;
}
+
return rc;
}
@@ -202,8 +441,13 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
{
if (crypto->dh_ctx != NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
DH_free(crypto->dh_ctx->keypair[0]);
DH_free(crypto->dh_ctx->keypair[1]);
+#else
+ EVP_PKEY_free(crypto->dh_ctx->keypair[0]);
+ EVP_PKEY_free(crypto->dh_ctx->keypair[1]);
+#endif /* OPENSSL_VERSION_NUMBER */
free(crypto->dh_ctx);
crypto->dh_ctx = NULL;
}
@@ -212,7 +456,8 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
/** @internal
* @brief generates a secret DH parameter of at least DH_SECURITY_BITS
* security as well as the corresponding public key.
- * @param[out] parms a dh_ctx that will hold the new keys.
+ *
+ * @param[out] params a dh_ctx that will hold the new keys.
* @param peer Select either client or server key storage. Valid values are:
* DH_CLIENT_KEYPAIR or DH_SERVER_KEYPAIR
*
@@ -221,14 +466,43 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer)
{
int rc;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif
if ((dh_ctx == NULL) || (dh_ctx->keypair[peer] == NULL)) {
return SSH_ERROR;
}
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
rc = DH_generate_key(dh_ctx->keypair[peer]);
if (rc != 1) {
return SSH_ERROR;
}
+#else
+ evp_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, dh_ctx->keypair[peer], NULL);
+ if (evp_ctx == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_keygen_init(evp_ctx);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(evp_ctx);
+ return SSH_ERROR;
+ }
+
+ rc = EVP_PKEY_generate(evp_ctx, &(dh_ctx->keypair[peer]));
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(evp_ctx);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to generate DH: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return SSH_ERROR;
+ }
+
+ EVP_PKEY_CTX_free(evp_ctx);
+#endif /* OPENSSL_VERSION_NUMBER */
+
return SSH_OK;
}
@@ -247,8 +521,14 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
bignum *dest)
{
unsigned char *kstring = NULL;
+ int rc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum pub_key = NULL;
- int klen, rc;
+ int klen;
+#else
+ size_t klen;
+ EVP_PKEY_CTX *evp_ctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
if ((dh_ctx == NULL) ||
(dh_ctx->keypair[local] == NULL) ||
@@ -256,6 +536,7 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
kstring = malloc(DH_size(dh_ctx->keypair[local]));
if (kstring == NULL) {
rc = SSH_ERROR;
@@ -273,6 +554,43 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
rc = SSH_ERROR;
goto done;
}
+#else
+ evp_ctx = EVP_PKEY_CTX_new_from_pkey(NULL, dh_ctx->keypair[local], NULL);
+
+ rc = EVP_PKEY_derive_init(evp_ctx);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ rc = EVP_PKEY_derive_set_peer(evp_ctx, dh_ctx->keypair[remote]);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to set peer key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ /* getting the size of the secret */
+ rc = EVP_PKEY_derive(evp_ctx, kstring, &klen);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ kstring = malloc(klen);
+ if (kstring == NULL) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+
+ rc = EVP_PKEY_derive(evp_ctx, kstring, &klen);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto done;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
*dest = BN_bin2bn(kstring, klen, NULL);
if (*dest == NULL) {
@@ -282,6 +600,9 @@ int ssh_dh_compute_shared_secret(struct dh_ctx *dh_ctx, int local, int remote,
rc = SSH_OK;
done:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_PKEY_CTX_free(evp_ctx);
+#endif
free(kstring);
return rc;
}
diff --git a/src/dh_key.c b/src/dh_key.c
index bda54b17..20d24a31 100644
--- a/src/dh_key.c
+++ b/src/dh_key.c
@@ -289,8 +289,10 @@ void ssh_dh_cleanup(struct ssh_crypto_struct *crypto)
/** @internal
* @brief generates a secret DH parameter of at least DH_SECURITY_BITS
* security as well as the corresponding public key.
- * @param[out] parms a dh_kex paramters structure with preallocated bignum
+ *
+ * @param[out] params a dh_kex parameters structure with preallocated bignum
* where to store the parameters
+ *
* @return SSH_OK on success, SSH_ERROR on error
*/
int ssh_dh_keypair_gen_keys(struct dh_ctx *dh_ctx, int peer)
diff --git a/src/ecdh.c b/src/ecdh.c
index a4c07ccb..af80beec 100644
--- a/src/ecdh.c
+++ b/src/ecdh.c
@@ -43,6 +43,11 @@ struct ssh_packet_callbacks_struct ssh_ecdh_client_callbacks = {
.user = NULL
};
+void ssh_client_ecdh_remove_callbacks(ssh_session session)
+{
+ ssh_packet_remove_callbacks(session, &ssh_ecdh_client_callbacks);
+}
+
/** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS
@@ -55,7 +60,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
(void)type;
(void)user;
- ssh_packet_remove_callbacks(session, &ssh_ecdh_client_callbacks);
+ ssh_client_ecdh_remove_callbacks(session);
pubkey_blob = ssh_buffer_get_ssh_string(packet);
if (pubkey_blob == NULL) {
ssh_set_error(session,SSH_FATAL, "No public key in packet");
@@ -88,16 +93,10 @@ SSH_PACKET_CALLBACK(ssh_packet_client_ecdh_reply){
}
/* Send the MSG_NEWKEYS */
- if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
- goto error;
- }
-
- rc=ssh_packet_send(session);
+ rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
-
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
diff --git a/src/ecdh_crypto.c b/src/ecdh_crypto.c
index a1de27fd..603c293b 100644
--- a/src/ecdh_crypto.c
+++ b/src/ecdh_crypto.c
@@ -30,15 +30,27 @@
#ifdef HAVE_ECDH
#include <openssl/ecdh.h>
-
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#define NISTP256 NID_X9_62_prime256v1
#define NISTP384 NID_secp384r1
#define NISTP521 NID_secp521r1
+#else
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/param_build.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include "libcrypto-compat.h"
+#endif /* OPENSSL_VERSION_NUMBER */
/** @internal
* @brief Map the given key exchange enum value to its curve name.
*/
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
+#else
+static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
+#endif /* OPENSSL_VERSION_NUMBER */
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
return NISTP256;
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
@@ -46,88 +58,191 @@ static int ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
return NISTP521;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
return SSH_ERROR;
+#else
+ return NULL;
+#endif
}
-/** @internal
- * @brief Starts ecdh-sha2-nistp256 key exchange
+/* @internal
+ * @brief Generate ECDH key pair for ecdh key exchange and store it in the
+ * session->next_crypto structure
*/
-int ssh_client_ecdh_init(ssh_session session){
- EC_KEY *key;
- const EC_GROUP *group;
- const EC_POINT *pubkey;
- ssh_string client_pubkey;
- int curve;
- int len;
- int rc;
- bignum_CTX ctx = BN_CTX_new();
+static ssh_string ssh_ecdh_generate(ssh_session session)
+{
+ ssh_string pubkey_string = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const EC_POINT *point = NULL;
+ const EC_GROUP *group = NULL;
+ EC_KEY *key = NULL;
+ int curve;
+#else
+ EC_POINT *point = NULL;
+ EC_GROUP *group = NULL;
+ const char *curve = NULL;
+ EVP_PKEY *key = NULL;
+ OSSL_PARAM *out_params = NULL;
+ const OSSL_PARAM *pubkey_param = NULL;
+ const void *pubkey = NULL;
+ size_t pubkey_len;
+ int nid;
+ int rc;
+#endif /* OPENSSL_VERSION_NUMBER */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
- if (rc < 0) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
+ curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (curve == SSH_ERROR) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to get curve name");
+ return NULL;
+ }
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == SSH_ERROR) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
+ key = EC_KEY_new_by_curve_name(curve);
+#else
+ if (curve == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to get curve name");
+ return NULL;
+ }
- key = EC_KEY_new_by_curve_name(curve);
- if (key == NULL) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
- group = EC_KEY_get0_group(key);
+ key = EVP_EC_gen(curve);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (key == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to generate key");
+ return NULL;
+ }
- EC_KEY_generate_key(key);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ group = EC_KEY_get0_group(key);
- pubkey=EC_KEY_get0_public_key(key);
- len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
- NULL,0,ctx);
+ EC_KEY_generate_key(key);
- client_pubkey = ssh_string_new(len);
- if (client_pubkey == NULL) {
- BN_CTX_free(ctx);
- EC_KEY_free(key);
- return SSH_ERROR;
- }
+ point = EC_KEY_get0_public_key(key);
- EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
- ssh_string_data(client_pubkey),len,ctx);
- BN_CTX_free(ctx);
+ pubkey_string = pki_key_make_ecpoint_string(group, point);
+#else
+ rc = EVP_PKEY_todata(key, EVP_PKEY_PUBLIC_KEY, &out_params);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to export public key");
+ EVP_PKEY_free(key);
+ return NULL;
+ }
- rc = ssh_buffer_add_ssh_string(session->out_buffer,client_pubkey);
- if (rc < 0) {
- EC_KEY_free(key);
- SSH_STRING_FREE(client_pubkey);
- return SSH_ERROR;
- }
+ pubkey_param = OSSL_PARAM_locate_const(out_params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (pubkey_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to find public key");
+ EVP_PKEY_free(key);
+ OSSL_PARAM_free(out_params);
+ return NULL;
+ }
- session->next_crypto->ecdh_privkey = key;
- session->next_crypto->ecdh_client_pubkey = client_pubkey;
+ rc = OSSL_PARAM_get_octet_string_ptr(pubkey_param,
+ (const void**)&pubkey,
+ &pubkey_len);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to read public key");
+ OSSL_PARAM_free(out_params);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
- /* register the packet callbacks */
- ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
- session->dh_handshake_state = DH_STATE_INIT_SENT;
+ /* Convert the data to low-level representation */
+ nid = pki_key_ecgroup_name_to_nid(curve);
+ group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, nid);
+ if (group == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not create group: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ OSSL_PARAM_free(out_params);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
+ point = EC_POINT_new(group);
+ if (point == NULL) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not create point: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EC_GROUP_free(group);
+ OSSL_PARAM_free(out_params);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
+ rc = EC_POINT_oct2point(group, point, pubkey, pubkey_len, NULL);
+ OSSL_PARAM_free(out_params);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to export public key");
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+ EVP_PKEY_free(key);
+ return NULL;
+ }
- rc = ssh_packet_send(session);
+ pubkey_string = pki_key_make_ecpoint_string(group, point);
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (pubkey_string == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to convert public key");
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(key);
+#else
+ EVP_PKEY_free(key);
+#endif /* OPENSSL_VERSION_NUMBER */
+ return NULL;
+ }
+ session->next_crypto->ecdh_privkey = key;
+ return pubkey_string;
+}
- return rc;
+/** @internal
+ * @brief Starts ecdh-sha2-nistp256 key exchange
+ */
+int ssh_client_ecdh_init(ssh_session session)
+{
+ ssh_string client_pubkey = NULL;
+ int rc;
+
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT);
+ if (rc < 0) {
+ return SSH_ERROR;
+ }
+
+ client_pubkey = ssh_ecdh_generate(session);
+ if (client_pubkey == NULL) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, client_pubkey);
+ if (rc < 0) {
+ ssh_string_free(client_pubkey);
+ return SSH_ERROR;
+ }
+
+ session->next_crypto->ecdh_client_pubkey = client_pubkey;
+
+ /* register the packet callbacks */
+ ssh_packet_set_callbacks(session, &ssh_ecdh_client_callbacks);
+ session->dh_handshake_state = DH_STATE_INIT_SENT;
+
+ rc = ssh_packet_send(session);
+
+ return rc;
}
-int ecdh_build_k(ssh_session session) {
- const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey);
- EC_POINT *pubkey;
- void *buffer;
+int ecdh_build_k(ssh_session session)
+{
+ struct ssh_crypto_struct *next_crypto = session->next_crypto;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const EC_GROUP *group = EC_KEY_get0_group(next_crypto->ecdh_privkey);
+ EC_POINT *pubkey = NULL;
+ void *buffer = NULL;
int rc;
int len = (EC_GROUP_get_degree(group) + 7) / 8;
bignum_CTX ctx = bignum_ctx_new();
if (ctx == NULL) {
return -1;
}
-
pubkey = EC_POINT_new(group);
if (pubkey == NULL) {
bignum_ctx_free(ctx);
@@ -137,14 +252,14 @@ int ecdh_build_k(ssh_session session) {
if (session->server) {
rc = EC_POINT_oct2point(group,
pubkey,
- ssh_string_data(session->next_crypto->ecdh_client_pubkey),
- ssh_string_len(session->next_crypto->ecdh_client_pubkey),
+ ssh_string_data(next_crypto->ecdh_client_pubkey),
+ ssh_string_len(next_crypto->ecdh_client_pubkey),
ctx);
} else {
rc = EC_POINT_oct2point(group,
pubkey,
- ssh_string_data(session->next_crypto->ecdh_server_pubkey),
- ssh_string_len(session->next_crypto->ecdh_server_pubkey),
+ ssh_string_data(next_crypto->ecdh_server_pubkey),
+ ssh_string_len(next_crypto->ecdh_server_pubkey),
ctx);
}
bignum_ctx_free(ctx);
@@ -162,7 +277,7 @@ int ecdh_build_k(ssh_session session) {
rc = ECDH_compute_key(buffer,
len,
pubkey,
- session->next_crypto->ecdh_privkey,
+ next_crypto->ecdh_privkey,
NULL);
EC_POINT_clear_free(pubkey);
if (rc <= 0) {
@@ -170,23 +285,150 @@ int ecdh_build_k(ssh_session session) {
return -1;
}
- bignum_bin2bn(buffer, len, &session->next_crypto->shared_secret);
+ bignum_bin2bn(buffer, len, &next_crypto->shared_secret);
free(buffer);
- if (session->next_crypto->shared_secret == NULL) {
- EC_KEY_free(session->next_crypto->ecdh_privkey);
- session->next_crypto->ecdh_privkey = NULL;
+#else
+ const char *curve = NULL;
+ EVP_PKEY *pubkey = NULL;
+ void *secret = NULL;
+ size_t secret_len;
+ int rc;
+ ssh_string peer_pubkey = NULL;
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ EVP_PKEY_CTX *dh_ctx = EVP_PKEY_CTX_new_from_pkey(NULL,
+ next_crypto->ecdh_privkey,
+ NULL);
+
+ if (dh_ctx == NULL || param_bld == NULL) {
+ ssh_set_error_oom(session);
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+ }
+
+ rc = EVP_PKEY_derive_init(dh_ctx);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not init PKEY derive: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
return -1;
}
- EC_KEY_free(session->next_crypto->ecdh_privkey);
- session->next_crypto->ecdh_privkey = NULL;
+
+ if (session->server) {
+ peer_pubkey = next_crypto->ecdh_client_pubkey;
+ } else {
+ peer_pubkey = next_crypto->ecdh_server_pubkey;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld,
+ OSSL_PKEY_PARAM_PUB_KEY,
+ ssh_string_data(peer_pubkey),
+ ssh_string_len(peer_pubkey));
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not push the pub key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+ }
+ curve = ecdh_kex_type_to_curve(next_crypto->kex_type);
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld,
+ OSSL_PKEY_PARAM_GROUP_NAME,
+ (char *)curve,
+ strlen(curve));
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not push the group name: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+ }
+
+ rc = evp_build_pkey("EC", param_bld, &pubkey, EVP_PKEY_PUBLIC_KEY);
+ OSSL_PARAM_BLD_free(param_bld);
+ if (rc != SSH_OK) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not build the pkey: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_derive_set_peer(dh_ctx, pubkey);
+ EVP_PKEY_free(pubkey);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not set peer pubkey: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ /* get the max length of the secret */
+ rc = EVP_PKEY_derive(dh_ctx, NULL, &secret_len);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not set peer pubkey: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ secret = malloc(secret_len);
+ if (secret == NULL) {
+ ssh_set_error_oom(session);
+ EVP_PKEY_CTX_free(dh_ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_derive(dh_ctx, secret, &secret_len);
+ if (rc != 1) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Could not derive shared key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_CTX_free(dh_ctx);
+ free(secret);
+ return -1;
+ }
+
+ EVP_PKEY_CTX_free(dh_ctx);
+
+ bignum_bin2bn(secret, secret_len, &next_crypto->shared_secret);
+ free(secret);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (next_crypto->shared_secret == NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(next_crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(next_crypto->ecdh_privkey);
+#endif /* OPENSSL_VERSION_NUMBER */
+ next_crypto->ecdh_privkey = NULL;
+ return -1;
+ }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY_free(next_crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(next_crypto->ecdh_privkey);
+#endif /* OPENSSL_VERSION_NUMBER */
+ next_crypto->ecdh_privkey = NULL;
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("Session server cookie",
- session->next_crypto->server_kex.cookie, 16);
+ next_crypto->server_kex.cookie, 16);
ssh_log_hexdump("Session client cookie",
- session->next_crypto->client_kex.cookie, 16);
- ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret);
-#endif
+ next_crypto->client_kex.cookie, 16);
+ ssh_print_bignum("Shared secret key", next_crypto->shared_secret);
+#endif /* DEBUG_CRYPTO */
return 0;
}
@@ -196,78 +438,36 @@ int ecdh_build_k(ssh_session session) {
/** @brief Handle a SSH_MSG_KEXDH_INIT packet (server) and send a
* SSH_MSG_KEXDH_REPLY
*/
-SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
+SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init)
+{
/* ECDH keys */
- ssh_string q_c_string;
- ssh_string q_s_string;
- EC_KEY *ecdh_key;
- const EC_GROUP *group;
- const EC_POINT *ecdh_pubkey;
- bignum_CTX ctx;
- /* SSH host keys (rsa,dsa,ecdsa) */
+ ssh_string q_c_string = NULL;
+ ssh_string q_s_string = NULL;
+ /* SSH host keys (rsa, ed25519 and ecdsa) */
ssh_key privkey;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
ssh_string pubkey_blob = NULL;
- int curve;
- int len;
int rc;
(void)type;
(void)user;
+ SSH_LOG(SSH_LOG_TRACE, "Processing SSH_MSG_KEXDH_INIT");
+
ssh_packet_remove_callbacks(session, &ssh_ecdh_server_callbacks);
/* Extract the client pubkey from the init packet */
q_c_string = ssh_buffer_get_ssh_string(packet);
if (q_c_string == NULL) {
- ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet");
+ ssh_set_error(session, SSH_FATAL, "No Q_C ECC point in packet");
goto error;
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
- /* Build server's keypair */
-
- ctx = BN_CTX_new();
-
- curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
- if (curve == SSH_ERROR) {
- BN_CTX_free(ctx);
- return SSH_ERROR;
- }
-
- ecdh_key = EC_KEY_new_by_curve_name(curve);
- if (ecdh_key == NULL) {
- ssh_set_error_oom(session);
- BN_CTX_free(ctx);
- goto error;
- }
-
- group = EC_KEY_get0_group(ecdh_key);
- EC_KEY_generate_key(ecdh_key);
-
- ecdh_pubkey = EC_KEY_get0_public_key(ecdh_key);
- len = EC_POINT_point2oct(group,
- ecdh_pubkey,
- POINT_CONVERSION_UNCOMPRESSED,
- NULL,
- 0,
- ctx);
-
- q_s_string = ssh_string_new(len);
+ q_s_string = ssh_ecdh_generate(session);
if (q_s_string == NULL) {
- EC_KEY_free(ecdh_key);
- BN_CTX_free(ctx);
goto error;
}
- EC_POINT_point2oct(group,
- ecdh_pubkey,
- POINT_CONVERSION_UNCOMPRESSED,
- ssh_string_data(q_s_string),
- len,
- ctx);
- BN_CTX_free(ctx);
-
- session->next_crypto->ecdh_privkey = ecdh_key;
session->next_crypto->ecdh_server_pubkey = q_s_string;
/* build k and session_id */
@@ -317,24 +517,18 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEXDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
goto error;
}
- /* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- goto error;
- }
-
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- if (rc == SSH_ERROR){
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return SSH_PACKET_USED;
error:
diff --git a/src/ecdh_gcrypt.c b/src/ecdh_gcrypt.c
index d9c41bf9..86d15f72 100644
--- a/src/ecdh_gcrypt.c
+++ b/src/ecdh_gcrypt.c
@@ -271,7 +271,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
ssh_string q_s_string;
gcry_sexp_t param = NULL;
gcry_sexp_t key = NULL;
- /* SSH host keys (rsa,dsa,ecdsa) */
+ /* SSH host keys (rsa, ed25519 and ecdsa) */
ssh_key privkey;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
@@ -295,7 +295,7 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
- /* Build server's keypair */
+ /* Build server's key pair */
err = gcry_sexp_build(&param, NULL, "(genkey(ecdh(curve %s) (flags transient-key)))",
curve);
if (err) {
@@ -366,23 +366,19 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEXDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc != SSH_OK) {
goto out;
}
-
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
/* Send the MSG_NEWKEYS */
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc != SSH_OK) {
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
-
out:
gcry_sexp_release(param);
gcry_sexp_release(key);
diff --git a/src/ecdh_mbedcrypto.c b/src/ecdh_mbedcrypto.c
index 718f1522..1d9c8f36 100644
--- a/src/ecdh_mbedcrypto.c
+++ b/src/ecdh_mbedcrypto.c
@@ -34,6 +34,7 @@
#include <mbedtls/ecdh.h>
#include <mbedtls/ecp.h>
+#include "mbedcrypto-compat.h"
#ifdef HAVE_ECDH
@@ -54,6 +55,10 @@ int ssh_client_ecdh_init(ssh_session session)
mbedtls_ecp_group grp;
int rc;
mbedtls_ecp_group_id curve;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ mbedtls_ecp_keypair *ecdh_privkey = NULL;
+
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
if (curve == MBEDTLS_ECP_DP_NONE) {
@@ -70,7 +75,9 @@ int ssh_client_ecdh_init(ssh_session session)
return SSH_ERROR;
}
- mbedtls_ecp_keypair_init(session->next_crypto->ecdh_privkey);
+ ecdh_privkey = session->next_crypto->ecdh_privkey;
+
+ mbedtls_ecp_keypair_init(ecdh_privkey);
mbedtls_ecp_group_init(&grp);
rc = mbedtls_ecp_group_load(&grp, curve);
@@ -80,10 +87,10 @@ int ssh_client_ecdh_init(ssh_session session)
}
rc = mbedtls_ecp_gen_keypair(&grp,
- &session->next_crypto->ecdh_privkey->d,
- &session->next_crypto->ecdh_privkey->Q,
- mbedtls_ctr_drbg_random,
- ssh_get_mbedtls_ctr_drbg_context());
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
@@ -91,7 +98,7 @@ int ssh_client_ecdh_init(ssh_session session)
}
client_pubkey = make_ecpoint_string(&grp,
- &session->next_crypto->ecdh_privkey->Q);
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q));
if (client_pubkey == NULL) {
rc = SSH_ERROR;
goto out;
@@ -124,6 +131,10 @@ int ecdh_build_k(ssh_session session)
mbedtls_ecp_point pubkey;
int rc;
mbedtls_ecp_group_id curve;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ mbedtls_ecp_keypair *ecdh_privkey = NULL;
+
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
curve = ecdh_kex_type_to_curve(session->next_crypto->kex_type);
if (curve == MBEDTLS_ECP_DP_NONE) {
@@ -162,19 +173,20 @@ int ecdh_build_k(ssh_session session)
mbedtls_mpi_init(session->next_crypto->shared_secret);
+ ecdh_privkey = session->next_crypto->ecdh_privkey;
rc = mbedtls_ecdh_compute_shared(&grp,
- session->next_crypto->shared_secret,
- &pubkey,
- &session->next_crypto->ecdh_privkey->d,
- mbedtls_ctr_drbg_random,
- ssh_get_mbedtls_ctr_drbg_context());
+ session->next_crypto->shared_secret,
+ &pubkey,
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
goto out;
}
out:
- mbedtls_ecp_keypair_free(session->next_crypto->ecdh_privkey);
+ mbedtls_ecp_keypair_free(ecdh_privkey);
SAFE_FREE(session->next_crypto->ecdh_privkey);
mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&pubkey);
@@ -187,6 +199,8 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
ssh_string q_c_string = NULL;
ssh_string q_s_string = NULL;
mbedtls_ecp_group grp;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+ mbedtls_ecp_keypair *ecdh_privkey = NULL;
ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string sig_blob = NULL;
@@ -214,10 +228,14 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
return SSH_ERROR;
}
+ ecdh_privkey = session->next_crypto->ecdh_privkey;
+
session->next_crypto->ecdh_client_pubkey = q_c_string;
+ ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
+
mbedtls_ecp_group_init(&grp);
- mbedtls_ecp_keypair_init(session->next_crypto->ecdh_privkey);
+ mbedtls_ecp_keypair_init(ecdh_privkey);
rc = mbedtls_ecp_group_load(&grp, curve);
if (rc != 0) {
@@ -226,16 +244,16 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
}
rc = mbedtls_ecp_gen_keypair(&grp,
- &session->next_crypto->ecdh_privkey->d,
- &session->next_crypto->ecdh_privkey->Q,
- mbedtls_ctr_drbg_random,
- ssh_get_mbedtls_ctr_drbg_context());
+ &ecdh_privkey->MBEDTLS_PRIVATE(d),
+ &ecdh_privkey->MBEDTLS_PRIVATE(Q),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
if (rc != 0) {
rc = SSH_ERROR;
goto out;
}
- q_s_string = make_ecpoint_string(&grp, &session->next_crypto->ecdh_privkey->Q);
+ q_s_string = make_ecpoint_string(&grp, &ecdh_privkey->MBEDTLS_PRIVATE(Q));
if (q_s_string == NULL) {
rc = SSH_ERROR;
goto out;
@@ -293,23 +311,20 @@ SSH_PACKET_CALLBACK(ssh_packet_server_ecdh_init){
goto out;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_KEXDH_REPLY sent");
rc = ssh_packet_send(session);
if (rc != SSH_OK) {
rc = SSH_ERROR;
goto out;
}
- rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
- if (rc < 0) {
- rc = SSH_ERROR;
+ session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_packet_send_newkeys(session);
+ if (rc == SSH_ERROR) {
goto out;
}
- session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
- rc = ssh_packet_send(session);
- SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
-
out:
mbedtls_ecp_group_free(&grp);
if (rc == SSH_ERROR) {
diff --git a/src/error.c b/src/error.c
index 22180407..3f8d78cd 100644
--- a/src/error.c
+++ b/src/error.c
@@ -29,7 +29,7 @@
#include "libssh/session.h"
/**
- * @defgroup libssh_error The SSH error functions.
+ * @defgroup libssh_error The SSH error functions
* @ingroup libssh
*
* Functions for error handling.
@@ -63,8 +63,8 @@ void _ssh_set_error(void *error,
va_end(va);
err->error.error_code = code;
- if (ssh_get_log_level() >= SSH_LOG_WARN) {
- ssh_log_function(SSH_LOG_WARN,
+ if (ssh_get_log_level() == SSH_LOG_TRACE) {
+ ssh_log_function(SSH_LOG_TRACE,
function,
err->error.error_buffer);
}
@@ -142,7 +142,7 @@ const char *ssh_get_error(void *error) {
* SSH_FATAL A fatal error occurred. This could be an unexpected
* disconnection\n
*
- * Other error codes are internal but can be considered same than
+ * Other error codes are internal but can be considered the same as
* SSH_FATAL.
*/
int ssh_get_error_code(void *error) {
diff --git a/src/external/bcrypt_pbkdf.c b/src/external/bcrypt_pbkdf.c
index 2208654d..85f4be47 100644
--- a/src/external/bcrypt_pbkdf.c
+++ b/src/external/bcrypt_pbkdf.c
@@ -42,7 +42,7 @@
* function with the following modifications:
* 1. The input password and salt are preprocessed with SHA512.
* 2. The output length is expanded to 256 bits.
- * 3. Subsequently the magic string to be encrypted is lengthened and modifed
+ * 3. Subsequently the magic string to be encrypted is lengthened and modified
* to "OxychromaticBlowfishSwatDynamite"
* 4. The hash function is defined to perform 64 rounds of initial state
* expansion. (More rounds are performed by iterating the hash.)
@@ -63,9 +63,8 @@
#define BCRYPT_HASHSIZE (BCRYPT_BLOCKS * 4)
static void
-bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
+bcrypt_hash(ssh_blf_ctx *state, uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
{
- ssh_blf_ctx state;
uint8_t ciphertext[BCRYPT_HASHSIZE] =
"OxychromaticBlowfishSwatDynamite";
uint32_t cdata[BCRYPT_BLOCKS];
@@ -74,11 +73,11 @@ bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
uint16_t shalen = SHA512_DIGEST_LENGTH;
/* key expansion */
- Blowfish_initstate(&state);
- Blowfish_expandstate(&state, sha2salt, shalen, sha2pass, shalen);
+ Blowfish_initstate(state);
+ Blowfish_expandstate(state, sha2salt, shalen, sha2pass, shalen);
for (i = 0; i < 64; i++) {
- Blowfish_expand0state(&state, sha2salt, shalen);
- Blowfish_expand0state(&state, sha2pass, shalen);
+ Blowfish_expand0state(state, sha2salt, shalen);
+ Blowfish_expand0state(state, sha2pass, shalen);
}
/* encryption */
@@ -87,7 +86,7 @@ bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
cdata[i] = Blowfish_stream2word(ciphertext, sizeof(ciphertext),
&j);
for (i = 0; i < 64; i++)
- ssh_blf_enc(&state, cdata, BCRYPT_BLOCKS/2);
+ ssh_blf_enc(state, cdata, BCRYPT_BLOCKS/2);
/* copy out */
for (i = 0; i < BCRYPT_BLOCKS; i++) {
@@ -100,7 +99,6 @@ bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
/* zap */
explicit_bzero(ciphertext, sizeof(ciphertext));
explicit_bzero(cdata, sizeof(cdata));
- ZERO_STRUCT(state);
}
int
@@ -115,6 +113,7 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
size_t i, j, amt, stride;
uint32_t count;
size_t origkeylen = keylen;
+ ssh_blf_ctx *state;
SHA512CTX ctx;
/* nothing crazy */
@@ -130,6 +129,12 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
memcpy(countsalt, salt, saltlen);
+ state = malloc(sizeof(*state));
+ if (state == NULL) {
+ free(countsalt);
+ return -1;
+ }
+
/* collapse password */
ctx = sha512_init();
sha512_update(ctx, pass, passlen);
@@ -147,7 +152,7 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
sha512_update(ctx, countsalt, saltlen + 4);
sha512_final(sha2salt, ctx);
- bcrypt_hash(sha2pass, sha2salt, tmpout);
+ bcrypt_hash(state, sha2pass, sha2salt, tmpout);
memcpy(out, tmpout, sizeof(out));
for (i = 1; i < rounds; i++) {
@@ -155,13 +160,13 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
ctx = sha512_init();
sha512_update(ctx, tmpout, sizeof(tmpout));
sha512_final(sha2salt, ctx);
- bcrypt_hash(sha2pass, sha2salt, tmpout);
+ bcrypt_hash(state, sha2pass, sha2salt, tmpout);
for (j = 0; j < sizeof(out); j++)
out[j] ^= tmpout[j];
}
/*
- * pbkdf2 deviation: ouput the key material non-linearly.
+ * pbkdf2 deviation: output the key material non-linearly.
*/
amt = MIN(amt, keylen);
for (i = 0; i < amt; i++) {
@@ -176,6 +181,9 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
/* zap */
explicit_bzero(out, sizeof(out));
+ explicit_bzero(state, sizeof(*state));
+
+ free(state);
free(countsalt);
return 0;
diff --git a/src/external/ed25519.c b/src/external/ed25519.c
index 8cd58591..41b5f289 100644
--- a/src/external/ed25519.c
+++ b/src/external/ed25519.c
@@ -77,8 +77,8 @@ static void get_hram(unsigned char *hram,
}
-int crypto_sign_ed25519_keypair(unsigned char *pk,
- unsigned char *sk)
+int crypto_sign_ed25519_keypair(ed25519_pubkey pk,
+ ed25519_privkey sk)
{
sc25519 scsk;
ge25519 gepk;
@@ -114,7 +114,7 @@ int crypto_sign_ed25519(unsigned char *sm,
uint64_t *smlen,
const unsigned char *m,
uint64_t mlen,
- const unsigned char *sk)
+ const ed25519_privkey sk)
{
sc25519 sck, scs, scsk;
ge25519 ger;
@@ -177,7 +177,7 @@ int crypto_sign_ed25519_open(unsigned char *m,
uint64_t *mlen,
const unsigned char *sm,
uint64_t smlen,
- const unsigned char *pk)
+ const ed25519_pubkey pk)
{
unsigned int i;
int ret;
diff --git a/src/gcrypt_missing.c b/src/gcrypt_missing.c
index e931ec5b..21a63a9b 100644
--- a/src/gcrypt_missing.c
+++ b/src/gcrypt_missing.c
@@ -55,7 +55,7 @@ char *ssh_gcry_bn2dec(bignum bn) {
size = gcry_mpi_get_nbits(bn) * 3;
rsize = size / 10 + size / 1000 + 2;
- ret = malloc(rsize + 1);
+ ret = gcry_malloc(rsize + 1);
if (ret == NULL) {
return NULL;
}
diff --git a/src/getpass.c b/src/getpass.c
index 99627665..6be33c77 100644
--- a/src/getpass.c
+++ b/src/getpass.c
@@ -44,7 +44,8 @@
*
* @return 1 on success, 0 on error.
*/
-static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) {
+static int ssh_gets(const char *prompt, char *buf, size_t len, int verify)
+{
char *tmp;
char *ptr = NULL;
int ok = 0;
@@ -121,7 +122,8 @@ int ssh_getpass(const char *prompt,
char *buf,
size_t len,
int echo,
- int verify) {
+ int verify)
+{
HANDLE h;
DWORD mode = 0;
int ok;
@@ -213,7 +215,8 @@ int ssh_getpass(const char *prompt,
char *buf,
size_t len,
int echo,
- int verify) {
+ int verify)
+{
struct termios attr;
struct termios old_attr;
int ok = 0;
@@ -255,7 +258,11 @@ int ssh_getpass(const char *prompt,
/* disable nonblocking I/O */
if (fd & O_NDELAY) {
- fcntl(0, F_SETFL, fd & ~O_NDELAY);
+ ok = fcntl(0, F_SETFL, fd & ~O_NDELAY);
+ if (ok < 0) {
+ perror("fcntl");
+ return -1;
+ }
}
ok = ssh_gets(prompt, buf, len, verify);
@@ -267,7 +274,11 @@ int ssh_getpass(const char *prompt,
/* close fd */
if (fd & O_NDELAY) {
- fcntl(0, F_SETFL, fd);
+ ok = fcntl(0, F_SETFL, fd);
+ if (ok < 0) {
+ perror("fcntl");
+ return -1;
+ }
}
if (!ok) {
diff --git a/src/getrandom_crypto.c b/src/getrandom_crypto.c
new file mode 100644
index 00000000..df8bd19f
--- /dev/null
+++ b/src/getrandom_crypto.c
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/crypto.h"
+#include <openssl/rand.h>
+
+/**
+ * @addtogroup libssh_misc
+ *
+ * @{
+ */
+
+/**
+ * @brief Get random bytes
+ *
+ * Make sure to always check the return code of this function!
+ *
+ * @param[in] where The buffer to fill with random bytes
+ *
+ * @param[in] len The size of the buffer to fill.
+ *
+ * @param[in] strong Use a strong or private RNG source.
+ *
+ * @return 1 on success, 0 on error.
+ */
+int
+ssh_get_random(void *where, int len, int strong)
+{
+#ifdef HAVE_OPENSSL_RAND_PRIV_BYTES
+ if (strong) {
+ /* Returns -1 when not supported, 0 on error, 1 on success */
+ return !!RAND_priv_bytes(where, len);
+ }
+#else
+ (void)strong;
+#endif /* HAVE_RAND_PRIV_BYTES */
+
+ /* Returns -1 when not supported, 0 on error, 1 on success */
+ return !!RAND_bytes(where, len);
+}
+
+/**
+ * @}
+ */
diff --git a/src/getrandom_gcrypt.c b/src/getrandom_gcrypt.c
new file mode 100644
index 00000000..da726405
--- /dev/null
+++ b/src/getrandom_gcrypt.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/crypto.h"
+#include <gcrypt.h>
+
+int
+ssh_get_random(void *where, int len, int strong)
+{
+ /* variable not used in gcrypt */
+ (void)strong;
+
+ /* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */
+ gcry_randomize(where, len, GCRY_STRONG_RANDOM);
+
+ return 1;
+}
diff --git a/src/getrandom_mbedcrypto.c b/src/getrandom_mbedcrypto.c
new file mode 100644
index 00000000..7e87b6a6
--- /dev/null
+++ b/src/getrandom_mbedcrypto.c
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/crypto.h"
+#include "mbedcrypto-compat.h"
+
+mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg;
+
+int
+ssh_mbedtls_random(void *where, int len, int strong)
+{
+ int rc = 0;
+ if (strong) {
+ mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
+ MBEDTLS_CTR_DRBG_PR_ON);
+ rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
+ mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
+ MBEDTLS_CTR_DRBG_PR_OFF);
+ } else {
+ rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
+ }
+
+ return !rc;
+}
+
+int
+ssh_get_random(void *where, int len, int strong)
+{
+ return ssh_mbedtls_random(where, len, strong);
+}
diff --git a/src/gssapi.c b/src/gssapi.c
index 488df582..5254d38d 100644
--- a/src/gssapi.c
+++ b/src/gssapi.c
@@ -68,15 +68,15 @@ struct ssh_gssapi_struct{
/** @internal
* @initializes a gssapi context for authentication
*/
-static int ssh_gssapi_init(ssh_session session){
+static int ssh_gssapi_init(ssh_session session)
+{
if (session->gssapi != NULL)
return SSH_OK;
- session->gssapi = malloc(sizeof(struct ssh_gssapi_struct));
- if(!session->gssapi){
+ session->gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
+ if (session->gssapi == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
- ZERO_STRUCTP(session->gssapi);
session->gssapi->server_creds = GSS_C_NO_CREDENTIAL;
session->gssapi->client_creds = GSS_C_NO_CREDENTIAL;
session->gssapi->ctx = GSS_C_NO_CONTEXT;
@@ -87,7 +87,8 @@ static int ssh_gssapi_init(ssh_session session){
/** @internal
* @frees a gssapi context
*/
-static void ssh_gssapi_free(ssh_session session){
+static void ssh_gssapi_free(ssh_session session)
+{
OM_uint32 min;
if (session->gssapi == NULL)
return;
@@ -114,7 +115,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token){
* @brief sends a SSH_MSG_USERAUTH_GSSAPI_RESPONSE packet
* @param[in] oid the OID that was selected for authentication
*/
-static int ssh_gssapi_send_response(ssh_session session, ssh_string oid){
+static int ssh_gssapi_send_response(ssh_session session, ssh_string oid)
+{
if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) < 0 ||
ssh_buffer_add_ssh_string(session->out_buffer,oid) < 0) {
ssh_set_error_oom(session);
@@ -184,8 +186,11 @@ out:
/** @internal
* @brief handles an user authentication using GSSAPI
*/
-int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids){
- char service_name[]="host";
+int
+ssh_gssapi_handle_userauth(ssh_session session, const char *user,
+ uint32_t n_oid, ssh_string *oids)
+{
+ char service_name[] = "host";
gss_buffer_desc name_buf;
gss_name_t server_name; /* local server fqdn */
OM_uint32 maj_stat, min_stat;
@@ -218,11 +223,12 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "indicate mecks %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG, "indicate mecks %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
+ gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
}
@@ -240,7 +246,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
continue;
}
if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){
- SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID");
+ SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
continue;
}
oid.elements = &oid_s[2];
@@ -253,25 +259,28 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
}
gss_release_oid_set(&min_stat, &supported);
if (oid_count == 0){
- SSH_LOG(SSH_LOG_PROTOCOL,"GSSAPI: no OID match");
+ SSH_LOG(SSH_LOG_DEBUG,"GSSAPI: no OID match");
ssh_auth_reply_default(session, 0);
gss_release_oid_set(&min_stat, &both_supported);
return SSH_OK;
}
/* from now we have room for context */
- if (ssh_gssapi_init(session) == SSH_ERROR)
+ if (ssh_gssapi_init(session) == SSH_ERROR) {
+ gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
+ }
name_buf.value = service_name;
name_buf.length = strlen(name_buf.value) + 1;
maj_stat = gss_import_name(&min_stat, &name_buf,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
+ gss_release_oid_set(&min_stat, &both_supported);
return -1;
}
@@ -282,8 +291,8 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
gss_release_oid_set(&min_stat, &both_supported);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "error acquiring credentials %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE, "error acquiring credentials %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_TRACE,
"acquiring creds",
maj_stat,
min_stat);
@@ -291,7 +300,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "acquiring credentials %d, %d", maj_stat, min_stat);
+ SSH_LOG(SSH_LOG_DEBUG, "acquiring credentials %d, %d", maj_stat, min_stat);
/* finding which OID from client we selected */
for (i=0 ; i< n_oid ; ++i){
@@ -302,7 +311,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
continue;
}
if(len < 2 || oid_s[0] != SSH_OID_TAG || ((size_t)oid_s[1]) != len - 2){
- SSH_LOG(SSH_LOG_WARNING,"GSSAPI: received invalid OID");
+ SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
continue;
}
oid.elements = &oid_s[2];
@@ -317,6 +326,7 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
session->gssapi->mech.elements = malloc(oid.length);
if (session->gssapi->mech.elements == NULL){
ssh_set_error_oom(session);
+ gss_release_oid_set(&min_stat, &selected);
return SSH_ERROR;
}
memcpy(session->gssapi->mech.elements, oid.elements, oid.length);
@@ -327,17 +337,19 @@ int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n
return ssh_gssapi_send_response(session, oids[i]);
}
-static char *ssh_gssapi_name_to_char(gss_name_t name){
+static char *ssh_gssapi_name_to_char(gss_name_t name)
+{
gss_buffer_desc buffer;
OM_uint32 maj_stat, min_stat;
char *ptr;
maj_stat = gss_display_name(&min_stat, name, &buffer, NULL);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"converting name",
maj_stat,
min_stat);
ptr = malloc(buffer.length + 1);
if (ptr == NULL) {
+ gss_release_buffer(&min_stat, &buffer);
return NULL;
}
memcpy(ptr, buffer.value, buffer.length);
@@ -407,7 +419,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
maj_stat = gss_accept_sec_context(&min_stat, &session->gssapi->ctx, session->gssapi->server_creds,
&input_token, input_bindings, &client_name, NULL /*mech_oid*/, &output_token, &ret_flags,
NULL /*time*/, &session->gssapi->client_creds);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token",
maj_stat,
min_stat);
@@ -417,10 +429,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
session->gssapi->canonic_user = ssh_gssapi_name_to_char(client_name);
}
if (GSS_ERROR(maj_stat)){
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
maj_stat,
min_stat);
+ gss_release_buffer(&min_stat, &output_token);
ssh_auth_reply_default(session,0);
ssh_gssapi_free(session);
session->gssapi=NULL;
@@ -431,13 +444,23 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa);
SAFE_FREE(hexa);
- ssh_buffer_pack(session->out_buffer,
- "bdP",
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- output_token.length,
- (size_t)output_token.length, output_token.value);
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdP",
+ SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ output_token.length,
+ (size_t)output_token.length, output_token.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ ssh_auth_reply_default(session, 0);
+ ssh_gssapi_free(session);
+ session->gssapi = NULL;
+ return SSH_PACKET_USED;
+ }
ssh_packet_send(session);
}
+
+ gss_release_buffer(&min_stat, &output_token);
+
if(maj_stat == GSS_S_COMPLETE){
session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC;
}
@@ -465,8 +488,8 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session)
rc = ssh_buffer_pack(mic_buffer,
"dPbsss",
- crypto->digest_len,
- (size_t)crypto->digest_len, crypto->session_id,
+ crypto->session_id_len,
+ crypto->session_id_len, crypto->session_id,
SSH2_MSG_USERAUTH_REQUEST,
session->gssapi->user,
"ssh-connection",
@@ -524,7 +547,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic)
mic_token_buf.value = ssh_string_data(mic_token);
maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"verifying MIC",
maj_stat,
min_stat);
@@ -572,7 +595,8 @@ end:
* @returns gssapi credentials handle.
* @returns NULL if no forwardable token is available.
*/
-ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){
+ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session)
+{
if (!session || !session->gssapi || session->gssapi->client_creds == GSS_C_NO_CREDENTIAL)
return NULL;
return (ssh_gssapi_creds)session->gssapi->client_creds;
@@ -581,10 +605,11 @@ ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session){
#endif /* SERVER */
/**
- * @brief Set the forwadable ticket to be given to the server for authentication.
+ * @brief Set the forwardable ticket to be given to the server for authentication.
* Unlike ssh_gssapi_get_creds() this is called on the client side of an ssh
* connection.
*
+ * @param[in] session The session
* @param[in] creds gssapi credentials handle.
*/
void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds)
@@ -602,7 +627,9 @@ void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds)
session->gssapi->client.client_deleg_creds = (gss_cred_id_t)creds;
}
-static int ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid){
+static int
+ssh_gssapi_send_auth_mic(ssh_session session, ssh_string *oid_set, int n_oid)
+{
int rc;
int i;
@@ -638,7 +665,7 @@ fail:
static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
{
OM_uint32 maj_stat, min_stat, lifetime;
- gss_OID_set actual_mechs;
+ gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
gss_buffer_desc namebuf;
gss_name_t client_id = GSS_C_NO_NAME;
gss_OID oid;
@@ -700,6 +727,7 @@ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
ret = SSH_OK;
end:
+ gss_release_oid_set(&min_stat, &actual_mechs);
gss_release_name(&min_stat, &client_id);
return ret;
}
@@ -711,9 +739,10 @@ end:
* SSH_AUTH_AGAIN: In nonblocking mode, you've got to call this again
* later.
*/
-int ssh_gssapi_auth_mic(ssh_session session){
+int ssh_gssapi_auth_mic(ssh_session session)
+{
size_t i;
- gss_OID_set selected; /* oid selected for authentication */
+ gss_OID_set selected = GSS_C_NO_OID_SET; /* oid selected for authentication */
ssh_string *oids = NULL;
int rc;
size_t n_oids = 0;
@@ -739,8 +768,8 @@ int ssh_gssapi_auth_mic(ssh_session session){
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
&session->gssapi->client.server_name);
if (maj_stat != GSS_S_COMPLETE) {
- SSH_LOG(SSH_LOG_WARNING, "importing name %d, %d", maj_stat, min_stat);
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
@@ -754,7 +783,7 @@ int ssh_gssapi_auth_mic(ssh_session session){
return SSH_AUTH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Authenticating with gssapi to host %s with user %s",
+ SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi to host %s with user %s",
session->opts.host, session->gssapi->user);
rc = ssh_gssapi_match(session, &selected);
if (rc == SSH_ERROR) {
@@ -762,7 +791,7 @@ int ssh_gssapi_auth_mic(ssh_session session){
}
n_oids = selected->count;
- SSH_LOG(SSH_LOG_PROTOCOL, "Sending %zu oids", n_oids);
+ SSH_LOG(SSH_LOG_DEBUG, "Sending %zu oids", n_oids);
oids = calloc(n_oids, sizeof(ssh_string));
if (oids == NULL) {
@@ -790,6 +819,8 @@ out:
SSH_STRING_FREE(oids[i]);
}
free(oids);
+ gss_release_oid_set(&min_stat, &selected);
+
if (rc != SSH_ERROR) {
return SSH_AUTH_AGAIN;
}
@@ -834,6 +865,7 @@ static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s)
}
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
+ int rc;
ssh_string oid_s;
gss_uint32 maj_stat, min_stat;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
@@ -875,7 +907,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
if(GSS_ERROR(maj_stat)){
- ssh_gssapi_log_error(SSH_LOG_WARNING,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Initializing gssapi context",
maj_stat,
min_stat);
@@ -885,14 +917,20 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s", hexa);
SAFE_FREE(hexa);
- ssh_buffer_pack(session->out_buffer,
- "bdP",
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- output_token.length,
- (size_t)output_token.length, output_token.value);
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdP",
+ SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ output_token.length,
+ (size_t)output_token.length, output_token.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
ssh_packet_send(session);
session->auth.state = SSH_AUTH_STATE_GSSAPI_TOKEN;
}
+
+ gss_release_buffer(&min_stat, &output_token);
return SSH_PACKET_USED;
error:
@@ -902,7 +940,8 @@ error:
return SSH_PACKET_USED;
}
-static int ssh_gssapi_send_mic(ssh_session session){
+static int ssh_gssapi_send_mic(ssh_session session)
+{
OM_uint32 maj_stat, min_stat;
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER;
@@ -921,9 +960,11 @@ static int ssh_gssapi_send_mic(ssh_session session){
maj_stat = gss_get_mic(&min_stat,session->gssapi->ctx, GSS_C_QOP_DEFAULT,
&mic_buf, &mic_token_buf);
+
+ SSH_BUFFER_FREE(mic_buffer);
+
if (GSS_ERROR(maj_stat)){
- SSH_BUFFER_FREE(mic_buffer);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"generating MIC",
maj_stat,
min_stat);
@@ -935,8 +976,10 @@ static int ssh_gssapi_send_mic(ssh_session session){
SSH2_MSG_USERAUTH_GSSAPI_MIC,
mic_token_buf.length,
(size_t)mic_token_buf.length, mic_token_buf.value);
+
+ gss_release_buffer(&min_stat, &mic_token_buf);
+
if (rc != SSH_OK) {
- SSH_BUFFER_FREE(mic_buffer);
ssh_set_error_oom(session);
return SSH_ERROR;
}
@@ -945,6 +988,7 @@ static int ssh_gssapi_send_mic(ssh_session session){
}
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
+ int rc;
ssh_string token;
char *hexa;
OM_uint32 maj_stat, min_stat;
@@ -980,13 +1024,13 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token",
maj_stat,
min_stat);
SSH_STRING_FREE(token);
if (GSS_ERROR(maj_stat)){
- ssh_gssapi_log_error(SSH_LOG_PROTOCOL,
+ ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
maj_stat,
min_stat);
@@ -997,14 +1041,20 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s",hexa);
SAFE_FREE(hexa);
- ssh_buffer_pack(session->out_buffer,
- "bdP",
- SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
- output_token.length,
- (size_t)output_token.length, output_token.value);
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bdP",
+ SSH2_MSG_USERAUTH_GSSAPI_TOKEN,
+ output_token.length,
+ (size_t)output_token.length, output_token.value);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
ssh_packet_send(session);
}
+ gss_release_buffer(&min_stat, &output_token);
+
if (maj_stat == GSS_S_COMPLETE) {
ssh_gssapi_send_mic(session);
session->auth.state = SSH_AUTH_STATE_GSSAPI_MIC_SENT;
diff --git a/src/gzip.c b/src/gzip.c
index f981de9b..cff15518 100644
--- a/src/gzip.c
+++ b/src/gzip.c
@@ -24,209 +24,237 @@
#include "config.h"
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <zlib.h>
-#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
+#include "libssh/priv.h"
#include "libssh/session.h"
+#ifndef BLOCKSIZE
#define BLOCKSIZE 4092
+#endif
-static z_stream *initcompress(ssh_session session, int level) {
- z_stream *stream = NULL;
- int status;
+static z_stream *
+initcompress(ssh_session session, int level)
+{
+ z_stream *stream = NULL;
+ int status;
- stream = calloc(1, sizeof(z_stream));
- if (stream == NULL) {
- return NULL;
- }
+ stream = calloc(1, sizeof(z_stream));
+ if (stream == NULL) {
+ return NULL;
+ }
- status = deflateInit(stream, level);
- if (status != Z_OK) {
- SAFE_FREE(stream);
- ssh_set_error(session, SSH_FATAL,
- "status %d inititalising zlib deflate", status);
- return NULL;
- }
+ status = deflateInit(stream, level);
+ if (status != Z_OK) {
+ SAFE_FREE(stream);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "status %d initialising zlib deflate",
+ status);
+ return NULL;
+ }
- return stream;
+ return stream;
}
-static ssh_buffer gzip_compress(ssh_session session, ssh_buffer source, int level)
+static ssh_buffer
+gzip_compress(ssh_session session, ssh_buffer source, int level)
{
- struct ssh_crypto_struct *crypto = NULL;
- z_stream *zout = NULL;
- void *in_ptr = ssh_buffer_get(source);
- unsigned long in_size = ssh_buffer_get_len(source);
- ssh_buffer dest = NULL;
- unsigned char out_buf[BLOCKSIZE] = {0};
- unsigned long len;
- int status;
-
- crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
- if (crypto == NULL) {
- return NULL;
- }
- zout = crypto->compress_out_ctx;
- if (zout == NULL) {
- zout = crypto->compress_out_ctx = initcompress(session, level);
+ struct ssh_crypto_struct *crypto = NULL;
+ z_stream *zout = NULL;
+ void *in_ptr = ssh_buffer_get(source);
+ uint32_t in_size = ssh_buffer_get_len(source);
+ ssh_buffer dest = NULL;
+ unsigned char out_buf[BLOCKSIZE] = {0};
+ uint32_t len;
+ int status;
+
+ crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
+ if (crypto == NULL) {
+ return NULL;
+ }
+ zout = crypto->compress_out_ctx;
if (zout == NULL) {
- return NULL;
+ zout = crypto->compress_out_ctx = initcompress(session, level);
+ if (zout == NULL) {
+ return NULL;
+ }
}
- }
-
- dest = ssh_buffer_new();
- if (dest == NULL) {
- return NULL;
- }
- zout->next_out = out_buf;
- zout->next_in = in_ptr;
- zout->avail_in = in_size;
- do {
- zout->avail_out = BLOCKSIZE;
- status = deflate(zout, Z_PARTIAL_FLUSH);
- if (status != Z_OK) {
- SSH_BUFFER_FREE(dest);
- ssh_set_error(session, SSH_FATAL,
- "status %d deflating zlib packet", status);
- return NULL;
- }
- len = BLOCKSIZE - zout->avail_out;
- if (ssh_buffer_add_data(dest, out_buf, len) < 0) {
- SSH_BUFFER_FREE(dest);
- return NULL;
+ dest = ssh_buffer_new();
+ if (dest == NULL) {
+ return NULL;
}
- zout->next_out = out_buf;
- } while (zout->avail_out == 0);
- return dest;
+ zout->next_out = out_buf;
+ zout->next_in = in_ptr;
+ zout->avail_in = in_size;
+ do {
+ zout->avail_out = BLOCKSIZE;
+ status = deflate(zout, Z_PARTIAL_FLUSH);
+ if (status != Z_OK) {
+ SSH_BUFFER_FREE(dest);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "status %d deflating zlib packet",
+ status);
+ return NULL;
+ }
+ len = BLOCKSIZE - zout->avail_out;
+ if (ssh_buffer_add_data(dest, out_buf, len) < 0) {
+ SSH_BUFFER_FREE(dest);
+ return NULL;
+ }
+ zout->next_out = out_buf;
+ } while (zout->avail_out == 0);
+
+ return dest;
}
-int compress_buffer(ssh_session session, ssh_buffer buf) {
- ssh_buffer dest = NULL;
+int
+compress_buffer(ssh_session session, ssh_buffer buf)
+{
+ ssh_buffer dest = NULL;
+ int rv;
- dest = gzip_compress(session, buf, session->opts.compressionlevel);
- if (dest == NULL) {
- return -1;
- }
+ dest = gzip_compress(session, buf, session->opts.compressionlevel);
+ if (dest == NULL) {
+ return -1;
+ }
- if (ssh_buffer_reinit(buf) < 0) {
- SSH_BUFFER_FREE(dest);
- return -1;
- }
+ if (ssh_buffer_reinit(buf) < 0) {
+ SSH_BUFFER_FREE(dest);
+ return -1;
+ }
- if (ssh_buffer_add_data(buf, ssh_buffer_get(dest), ssh_buffer_get_len(dest)) < 0) {
- SSH_BUFFER_FREE(dest);
- return -1;
- }
+ rv = ssh_buffer_add_data(buf,
+ ssh_buffer_get(dest),
+ ssh_buffer_get_len(dest));
+ if (rv < 0) {
+ SSH_BUFFER_FREE(dest);
+ return -1;
+ }
- SSH_BUFFER_FREE(dest);
- return 0;
+ SSH_BUFFER_FREE(dest);
+ return 0;
}
/* decompression */
-static z_stream *initdecompress(ssh_session session) {
- z_stream *stream = NULL;
- int status;
+static z_stream *
+initdecompress(ssh_session session)
+{
+ z_stream *stream = NULL;
+ int status;
- stream = calloc(1, sizeof(z_stream));
- if (stream == NULL) {
- return NULL;
- }
+ stream = calloc(1, sizeof(z_stream));
+ if (stream == NULL) {
+ return NULL;
+ }
- status = inflateInit(stream);
- if (status != Z_OK) {
- SAFE_FREE(stream);
- ssh_set_error(session, SSH_FATAL,
- "Status = %d initiating inflate context!", status);
- return NULL;
- }
+ status = inflateInit(stream);
+ if (status != Z_OK) {
+ SAFE_FREE(stream);
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Status = %d initiating inflate context!",
+ status);
+ return NULL;
+ }
- return stream;
+ return stream;
}
-static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen)
+static ssh_buffer
+gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen)
{
- struct ssh_crypto_struct *crypto = NULL;
- z_stream *zin = NULL;
- void *in_ptr = ssh_buffer_get(source);
- unsigned long in_size = ssh_buffer_get_len(source);
- unsigned char out_buf[BLOCKSIZE] = {0};
- ssh_buffer dest = NULL;
- unsigned long len;
- int status;
-
- crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
- if (crypto == NULL) {
- return NULL;
- }
-
- zin = crypto->compress_in_ctx;
- if (zin == NULL) {
- zin = crypto->compress_in_ctx = initdecompress(session);
- if (zin == NULL) {
- return NULL;
+ struct ssh_crypto_struct *crypto = NULL;
+ z_stream *zin = NULL;
+ void *in_ptr = ssh_buffer_get(source);
+ uint32_t in_size = ssh_buffer_get_len(source);
+ unsigned char out_buf[BLOCKSIZE] = {0};
+ ssh_buffer dest = NULL;
+ uint32_t len;
+ int status;
+
+ crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
+ if (crypto == NULL) {
+ return NULL;
}
- }
-
- dest = ssh_buffer_new();
- if (dest == NULL) {
- return NULL;
- }
-
- zin->next_out = out_buf;
- zin->next_in = in_ptr;
- zin->avail_in = in_size;
- do {
- zin->avail_out = BLOCKSIZE;
- status = inflate(zin, Z_PARTIAL_FLUSH);
- if (status != Z_OK && status != Z_BUF_ERROR) {
- ssh_set_error(session, SSH_FATAL,
- "status %d inflating zlib packet", status);
- SSH_BUFFER_FREE(dest);
- return NULL;
+ zin = crypto->compress_in_ctx;
+ if (zin == NULL) {
+ zin = crypto->compress_in_ctx = initdecompress(session);
+ if (zin == NULL) {
+ return NULL;
+ }
}
- len = BLOCKSIZE - zin->avail_out;
- if (ssh_buffer_add_data(dest,out_buf,len) < 0) {
- SSH_BUFFER_FREE(dest);
- return NULL;
- }
- if (ssh_buffer_get_len(dest) > maxlen){
- /* Size of packet exceeded, avoid a denial of service attack */
- SSH_BUFFER_FREE(dest);
- return NULL;
+ dest = ssh_buffer_new();
+ if (dest == NULL) {
+ return NULL;
}
- zin->next_out = out_buf;
- } while (zin->avail_out == 0);
- return dest;
+ zin->next_out = out_buf;
+ zin->next_in = in_ptr;
+ zin->avail_in = in_size;
+
+ do {
+ zin->avail_out = BLOCKSIZE;
+ status = inflate(zin, Z_PARTIAL_FLUSH);
+ if (status != Z_OK && status != Z_BUF_ERROR) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "status %d inflating zlib packet",
+ status);
+ SSH_BUFFER_FREE(dest);
+ return NULL;
+ }
+
+ len = BLOCKSIZE - zin->avail_out;
+ if (ssh_buffer_add_data(dest, out_buf, len) < 0) {
+ SSH_BUFFER_FREE(dest);
+ return NULL;
+ }
+ if (ssh_buffer_get_len(dest) > maxlen) {
+ /* Size of packet exceeded, avoid a denial of service attack */
+ SSH_BUFFER_FREE(dest);
+ return NULL;
+ }
+ zin->next_out = out_buf;
+ } while (zin->avail_out == 0);
+
+ return dest;
}
-int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){
- ssh_buffer dest = NULL;
+int
+decompress_buffer(ssh_session session, ssh_buffer buf, size_t maxlen)
+{
+ ssh_buffer dest = NULL;
+ int rv;
- dest = gzip_decompress(session,buf, maxlen);
- if (dest == NULL) {
- return -1;
- }
+ dest = gzip_decompress(session, buf, maxlen);
+ if (dest == NULL) {
+ return -1;
+ }
- if (ssh_buffer_reinit(buf) < 0) {
- SSH_BUFFER_FREE(dest);
- return -1;
- }
+ if (ssh_buffer_reinit(buf) < 0) {
+ SSH_BUFFER_FREE(dest);
+ return -1;
+ }
- if (ssh_buffer_add_data(buf, ssh_buffer_get(dest), ssh_buffer_get_len(dest)) < 0) {
- SSH_BUFFER_FREE(dest);
- return -1;
- }
+ rv = ssh_buffer_add_data(buf,
+ ssh_buffer_get(dest),
+ ssh_buffer_get_len(dest));
+ if (rv < 0) {
+ SSH_BUFFER_FREE(dest);
+ return -1;
+ }
- SSH_BUFFER_FREE(dest);
- return 0;
+ SSH_BUFFER_FREE(dest);
+ return 0;
}
diff --git a/src/init.c b/src/init.c
index 2ebcedf6..e516c331 100644
--- a/src/init.c
+++ b/src/init.c
@@ -22,6 +22,9 @@
*/
#include "config.h"
+
+#include <stdio.h>
+
#include "libssh/priv.h"
#include "libssh/socket.h"
#include "libssh/dh.h"
@@ -104,7 +107,7 @@ _ret:
/**
* @brief Initialize global cryptographic data structures.
*
- * This functions is automatically called when the library is loaded.
+ * This function is automatically called when the library is loaded.
*
*/
void libssh_constructor(void)
@@ -127,7 +130,7 @@ void libssh_constructor(void)
* The libssh library is implementing the SSH protocols and some of its
* extensions. This group of functions is mostly used to implement an SSH
* client.
- * Some function are needed to implement an SSH server too.
+ * Some functions are needed to implement an SSH server too.
*
* @{
*/
@@ -136,8 +139,8 @@ void libssh_constructor(void)
* @brief Initialize global cryptographic data structures.
*
* Since version 0.8.0, when libssh is dynamically linked, it is not necessary
- * to call this function on systems which are fully supported with regards to
- * threading (that is, system with pthreads available).
+ * to call this function on systems that fully support threading (that is,
+ * systems with pthreads available).
*
* If libssh is statically linked, it is necessary to explicitly call ssh_init()
* before calling any other provided API, and it is necessary to explicitly call
@@ -161,12 +164,14 @@ static int _ssh_finalize(unsigned destructor) {
if (_ssh_initialized > 1) {
_ssh_initialized--;
- goto _ret;
+ ssh_mutex_unlock(&ssh_init_mutex);
+ return 0;
}
if (_ssh_initialized == 1) {
if (_ssh_init_ret < 0) {
- goto _ret;
+ ssh_mutex_unlock(&ssh_init_mutex);
+ return 0;
}
}
}
@@ -181,15 +186,22 @@ static int _ssh_finalize(unsigned destructor) {
_ssh_initialized = 0;
-_ret:
if (!destructor) {
ssh_mutex_unlock(&ssh_init_mutex);
}
+
+#if (defined(_WIN32) && !defined(HAVE_PTHREAD))
+ if (ssh_init_mutex != NULL) {
+ DeleteCriticalSection(ssh_init_mutex);
+ SAFE_FREE(ssh_init_mutex);
+ }
+#endif
+
return 0;
}
/**
- * @brief Finalize and cleanup all libssh and cryptographic data structures.
+ * @brief Finalize and clean up all libssh and cryptographic data structures.
*
* This function is automatically called when the library is unloaded.
*
@@ -206,7 +218,7 @@ void libssh_destructor(void)
}
/**
- * @brief Finalize and cleanup all libssh and cryptographic data structures.
+ * @brief Finalize and clean up all libssh and cryptographic data structures.
*
* Since version 0.8.0, when libssh is dynamically linked, it is not necessary
* to call this function, since it is automatically called when the library is
@@ -269,7 +281,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,
*
* @see ssh_init()
*/
-bool is_ssh_initialized() {
+bool is_ssh_initialized(void) {
bool is_initialized = false;
diff --git a/src/kdf.c b/src/kdf.c
index 0e90e188..6bc477ce 100644
--- a/src/kdf.c
+++ b/src/kdf.c
@@ -39,7 +39,7 @@
/* The following implements the SSHKDF for crypto backend that
- * do not have a native implementations */
+ * do not have a native implementation */
struct ssh_mac_ctx_struct {
enum ssh_kdf_digest digest_type;
union {
@@ -58,73 +58,110 @@ static ssh_mac_ctx ssh_mac_ctx_init(enum ssh_kdf_digest type)
}
ctx->digest_type = type;
- switch(type){
+ switch (type) {
case SSH_KDF_SHA1:
ctx->ctx.sha1_ctx = sha1_init();
+ if (ctx->ctx.sha1_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA256:
ctx->ctx.sha256_ctx = sha256_init();
+ if (ctx->ctx.sha256_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA384:
ctx->ctx.sha384_ctx = sha384_init();
+ if (ctx->ctx.sha384_ctx == NULL) {
+ goto err;
+ }
return ctx;
case SSH_KDF_SHA512:
ctx->ctx.sha512_ctx = sha512_init();
+ if (ctx->ctx.sha512_ctx == NULL) {
+ goto err;
+ }
return ctx;
- default:
- SAFE_FREE(ctx);
- return NULL;
}
+err:
+ SAFE_FREE(ctx);
+ return NULL;
}
-static void ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len)
+static void ssh_mac_ctx_free(ssh_mac_ctx ctx)
{
- switch(ctx->digest_type){
+ if (ctx == NULL) {
+ return;
+ }
+
+ switch (ctx->digest_type) {
case SSH_KDF_SHA1:
- sha1_update(ctx->ctx.sha1_ctx, data, len);
+ sha1_ctx_free(ctx->ctx.sha1_ctx);
break;
case SSH_KDF_SHA256:
- sha256_update(ctx->ctx.sha256_ctx, data, len);
+ sha256_ctx_free(ctx->ctx.sha256_ctx);
break;
case SSH_KDF_SHA384:
- sha384_update(ctx->ctx.sha384_ctx, data, len);
+ sha384_ctx_free(ctx->ctx.sha384_ctx);
break;
case SSH_KDF_SHA512:
- sha512_update(ctx->ctx.sha512_ctx, data, len);
+ sha512_ctx_free(ctx->ctx.sha512_ctx);
break;
}
+ SAFE_FREE(ctx);
}
-static void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+static int ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len)
{
- switch(ctx->digest_type){
+ switch (ctx->digest_type) {
+ case SSH_KDF_SHA1:
+ return sha1_update(ctx->ctx.sha1_ctx, data, len);
+ case SSH_KDF_SHA256:
+ return sha256_update(ctx->ctx.sha256_ctx, data, len);
+ case SSH_KDF_SHA384:
+ return sha384_update(ctx->ctx.sha384_ctx, data, len);
+ case SSH_KDF_SHA512:
+ return sha512_update(ctx->ctx.sha512_ctx, data, len);
+ }
+ return SSH_ERROR;
+}
+
+static int ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx)
+{
+ int rc = SSH_ERROR;
+
+ switch (ctx->digest_type) {
case SSH_KDF_SHA1:
- sha1_final(md,ctx->ctx.sha1_ctx);
+ rc = sha1_final(md, ctx->ctx.sha1_ctx);
break;
case SSH_KDF_SHA256:
- sha256_final(md,ctx->ctx.sha256_ctx);
+ rc = sha256_final(md, ctx->ctx.sha256_ctx);
break;
case SSH_KDF_SHA384:
- sha384_final(md,ctx->ctx.sha384_ctx);
+ rc = sha384_final(md, ctx->ctx.sha384_ctx);
break;
case SSH_KDF_SHA512:
- sha512_final(md,ctx->ctx.sha512_ctx);
+ rc = sha512_final(md, ctx->ctx.sha512_ctx);
break;
}
SAFE_FREE(ctx);
+ return rc;
}
int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
- unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ unsigned char *key,
+ size_t key_len,
+ uint8_t key_type,
+ unsigned char *output,
size_t requested_len)
{
/* Can't use VLAs with Visual Studio, so allocate the biggest
* digest buffer we can possibly need */
unsigned char digest[DIGEST_MAX_LEN];
size_t output_len = crypto->digest_len;
- char letter = key_type;
ssh_mac_ctx ctx;
+ int rc;
if (DIGEST_MAX_LEN < crypto->digest_len) {
return -1;
@@ -135,11 +172,30 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
return -1;
}
- ssh_mac_update(ctx, key, key_len);
- ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
- ssh_mac_update(ctx, &letter, 1);
- ssh_mac_update(ctx, crypto->session_id, crypto->digest_len);
- ssh_mac_final(digest, ctx);
+ rc = ssh_mac_update(ctx, key, key_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, &key_type, 1);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->session_id, crypto->session_id_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_final(digest, ctx);
+ if (rc != SSH_OK) {
+ return -1;
+ }
if (requested_len < output_len) {
output_len = requested_len;
@@ -151,10 +207,25 @@ int sshkdf_derive_key(struct ssh_crypto_struct *crypto,
if (ctx == NULL) {
return -1;
}
- ssh_mac_update(ctx, key, key_len);
- ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
- ssh_mac_update(ctx, output, output_len);
- ssh_mac_final(digest, ctx);
+ rc = ssh_mac_update(ctx, key, key_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_update(ctx, output, output_len);
+ if (rc != SSH_OK) {
+ ssh_mac_ctx_free(ctx);
+ return -1;
+ }
+ rc = ssh_mac_final(digest, ctx);
+ if (rc != SSH_OK) {
+ return -1;
+ }
if (requested_len < output_len + crypto->digest_len) {
memcpy(output + output_len, digest, requested_len - output_len);
} else {
diff --git a/src/kex.c b/src/kex.c
index c2c59de2..b071d5ea 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <stdbool.h>
+#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/dh.h"
@@ -47,7 +48,7 @@
#ifdef WITH_BLOWFISH_CIPHER
# if defined(HAVE_OPENSSL_BLOWFISH_H) || defined(HAVE_LIBGCRYPT) || defined(HAVE_LIBMBEDCRYPTO)
-# define BLOWFISH "blowfish-cbc,"
+# define BLOWFISH ",blowfish-cbc"
# else
# define BLOWFISH ""
# endif
@@ -57,10 +58,9 @@
#ifdef HAVE_LIBGCRYPT
# define AES "aes256-gcm@openssh.com,aes128-gcm@openssh.com," \
- "aes256-ctr,aes192-ctr,aes128-ctr,"
-# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
-# define DES "3des-cbc"
-# define DES_SUPPORTED "3des-cbc"
+ "aes256-ctr,aes192-ctr,aes128-ctr"
+# define AES_CBC ",aes256-cbc,aes192-cbc,aes128-cbc"
+# define DES_SUPPORTED ",3des-cbc"
#elif defined(HAVE_LIBMBEDCRYPTO)
# ifdef MBEDTLS_GCM_C
@@ -68,89 +68,82 @@
# else
# define GCM ""
# endif /* MBEDTLS_GCM_C */
-# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr,"
-# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
-# define DES "3des-cbc"
-# define DES_SUPPORTED "3des-cbc"
+# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr"
+# define AES_CBC ",aes256-cbc,aes192-cbc,aes128-cbc"
+# define DES_SUPPORTED ",3des-cbc"
#elif defined(HAVE_LIBCRYPTO)
# ifdef HAVE_OPENSSL_AES_H
-# ifdef HAVE_OPENSSL_EVP_AES_GCM
-# define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com,"
-# else
-# define GCM ""
-# endif /* HAVE_OPENSSL_EVP_AES_GCM */
-# ifdef BROKEN_AES_CTR
-# define AES GCM
-# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
-# else /* BROKEN_AES_CTR */
-# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr,"
-# define AES_CBC "aes256-cbc,aes192-cbc,aes128-cbc,"
-# endif /* BROKEN_AES_CTR */
+# define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com,"
+# define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr"
+# define AES_CBC ",aes256-cbc,aes192-cbc,aes128-cbc"
# else /* HAVE_OPENSSL_AES_H */
# define AES ""
# define AES_CBC ""
# endif /* HAVE_OPENSSL_AES_H */
-# define DES "3des-cbc"
-# define DES_SUPPORTED "3des-cbc"
+# define DES_SUPPORTED ",3des-cbc"
#endif /* HAVE_LIBCRYPTO */
#ifdef WITH_ZLIB
-#define ZLIB "none,zlib,zlib@openssh.com"
+#define ZLIB "none,zlib@openssh.com,zlib"
+#define ZLIB_DEFAULT "none,zlib@openssh.com"
#else
#define ZLIB "none"
-#endif
+#define ZLIB_DEFAULT "none"
+#endif /* WITH_ZLIB */
#ifdef HAVE_CURVE25519
#define CURVE25519 "curve25519-sha256,curve25519-sha256@libssh.org,"
#else
#define CURVE25519 ""
-#endif
+#endif /* HAVE_CURVE25519 */
-#ifdef HAVE_ECDH
+#ifdef HAVE_ECC
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
-#define EC_HOSTKEYS "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,"
-#define EC_PUBLIC_KEY_ALGORITHMS "ecdsa-sha2-nistp521-cert-v01@openssh.com," \
+#define EC_HOSTKEYS "ecdsa-sha2-nistp521," \
+ "ecdsa-sha2-nistp384," \
+ "ecdsa-sha2-nistp256,"
+#define EC_SK_HOSTKEYS "sk-ecdsa-sha2-nistp256@openssh.com,"
+#define EC_FIPS_PUBLIC_KEY_ALGOS "ecdsa-sha2-nistp521-cert-v01@openssh.com," \
"ecdsa-sha2-nistp384-cert-v01@openssh.com," \
"ecdsa-sha2-nistp256-cert-v01@openssh.com,"
+#define EC_PUBLIC_KEY_ALGORITHMS EC_FIPS_PUBLIC_KEY_ALGOS \
+ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,"
#else
+#define ECDH ""
#define EC_HOSTKEYS ""
+#define EC_SK_HOSTKEYS ""
+#define EC_FIPS_PUBLIC_KEY_ALGOS ""
#define EC_PUBLIC_KEY_ALGORITHMS ""
-#define ECDH ""
-#endif
-
-#ifdef HAVE_DSA
-#define DSA_HOSTKEYS ",ssh-dss"
-#define DSA_PUBLIC_KEY_ALGORITHMS ",ssh-dss-cert-v01@openssh.com"
-#else
-#define DSA_HOSTKEYS ""
-#define DSA_PUBLIC_KEY_ALGORITHMS ""
-#endif
+#endif /* HAVE_ECC */
#ifdef WITH_INSECURE_NONE
#define NONE ",none"
#else
#define NONE
-#endif
+#endif /* WITH_INSECURE_NONE */
#define HOSTKEYS "ssh-ed25519," \
EC_HOSTKEYS \
+ "sk-ssh-ed25519@openssh.com," \
+ EC_SK_HOSTKEYS \
"rsa-sha2-512," \
"rsa-sha2-256," \
- "ssh-rsa" \
- DSA_HOSTKEYS
+ "ssh-rsa"
#define DEFAULT_HOSTKEYS "ssh-ed25519," \
EC_HOSTKEYS \
+ "sk-ssh-ed25519@openssh.com," \
+ EC_SK_HOSTKEYS \
"rsa-sha2-512," \
"rsa-sha2-256"
#define PUBLIC_KEY_ALGORITHMS "ssh-ed25519-cert-v01@openssh.com," \
+ "sk-ssh-ed25519-cert-v01@openssh.com," \
EC_PUBLIC_KEY_ALGORITHMS \
"rsa-sha2-512-cert-v01@openssh.com," \
"rsa-sha2-256-cert-v01@openssh.com," \
- "ssh-rsa-cert-v01@openssh.com" \
- DSA_PUBLIC_KEY_ALGORITHMS "," \
+ "ssh-rsa-cert-v01@openssh.com," \
HOSTKEYS
#define DEFAULT_PUBLIC_KEY_ALGORITHMS "ssh-ed25519-cert-v01@openssh.com," \
EC_PUBLIC_KEY_ALGORITHMS \
@@ -168,19 +161,23 @@
#define CHACHA20 "chacha20-poly1305@openssh.com,"
-#define KEY_EXCHANGE \
+#define DEFAULT_KEY_EXCHANGE \
CURVE25519 \
ECDH \
"diffie-hellman-group18-sha512,diffie-hellman-group16-sha512," \
GEX_SHA256 \
- "diffie-hellman-group14-sha256," \
- "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+ "diffie-hellman-group14-sha256" \
+
#define KEY_EXCHANGE_SUPPORTED \
GEX_SHA1 \
- KEY_EXCHANGE
+ DEFAULT_KEY_EXCHANGE \
+ ",diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
/* RFC 8308 */
#define KEX_EXTENSION_CLIENT "ext-info-c"
+/* Strict kex mitigation against CVE-2023-48795 */
+#define KEX_STRICT_CLIENT "kex-strict-c-v00@openssh.com"
+#define KEX_STRICT_SERVER "kex-strict-s-v00@openssh.com"
/* Allowed algorithms in FIPS mode */
#define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\
@@ -194,7 +191,7 @@
"rsa-sha2-512," \
"rsa-sha2-256"
-#define FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS EC_PUBLIC_KEY_ALGORITHMS \
+#define FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS EC_FIPS_PUBLIC_KEY_ALGOS \
"rsa-sha2-512-cert-v01@openssh.com," \
"rsa-sha2-256-cert-v01@openssh.com," \
FIPS_ALLOWED_HOSTKEYS
@@ -222,8 +219,8 @@ static const char *fips_methods[] = {
FIPS_ALLOWED_CIPHERS,
FIPS_ALLOWED_MACS,
FIPS_ALLOWED_MACS,
- ZLIB,
- ZLIB,
+ ZLIB_DEFAULT,
+ ZLIB_DEFAULT,
"",
"",
NULL
@@ -231,14 +228,14 @@ static const char *fips_methods[] = {
/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
static const char *default_methods[] = {
- KEY_EXCHANGE,
+ DEFAULT_KEY_EXCHANGE,
DEFAULT_PUBLIC_KEY_ALGORITHMS,
- CHACHA20 AES DES,
- CHACHA20 AES DES,
- "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1",
- "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1",
- "none",
- "none",
+ CHACHA20 AES,
+ CHACHA20 AES,
+ "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512",
+ "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512",
+ ZLIB_DEFAULT,
+ ZLIB_DEFAULT,
"",
"",
NULL
@@ -324,6 +321,10 @@ static int cmp_first_kex_algo(const char *client_str,
int is_wrong = 1;
+ if (client_str == NULL || server_str == NULL) {
+ return is_wrong;
+ }
+
colon = strchr(client_str, ',');
if (colon == NULL) {
client_kex_len = strlen(client_str);
@@ -350,6 +351,7 @@ static int cmp_first_kex_algo(const char *client_str,
SSH_PACKET_CALLBACK(ssh_packet_kexinit)
{
int i, ok;
+ struct ssh_crypto_struct *crypto = session->next_crypto;
int server_kex = session->server;
ssh_string str = NULL;
char *strings[SSH_KEX_METHODS] = {0};
@@ -363,35 +365,67 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
(void)type;
(void)user;
+ SSH_LOG(SSH_LOG_TRACE, "KEXINIT received");
+
if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) {
- SSH_LOG(SSH_LOG_INFO, "Initiating key re-exchange");
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
+ SSH_LOG(SSH_LOG_DEBUG, "Peer initiated key re-exchange");
+ /* Reset the sent flag if the re-kex was initiated by the peer */
+ session->flags &= ~SSH_SESSION_FLAG_KEXINIT_SENT;
+ } else if (session->flags & SSH_SESSION_FLAG_KEXINIT_SENT &&
+ session->dh_handshake_state == DH_STATE_INIT_SENT) {
+ /* This happens only when we are sending our-guessed first kex
+ * packet right after our KEXINIT packet. */
+ SSH_LOG(SSH_LOG_DEBUG, "Received peer kexinit answer.");
+ } else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) {
+ ssh_set_error(session, SSH_FATAL,
+ "SSH_KEXINIT received in wrong state");
+ goto error;
+ }
} else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) {
- ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
+ ssh_set_error(session, SSH_FATAL,
+ "SSH_KEXINIT received in wrong state");
goto error;
}
if (server_kex) {
- len = ssh_buffer_get_data(packet,session->next_crypto->client_kex.cookie, 16);
+#ifdef WITH_SERVER
+ len = ssh_buffer_get_data(packet, crypto->client_kex.cookie, 16);
if (len != 16) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: no cookie in packet");
goto error;
}
- ok = ssh_hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie);
+ ok = ssh_hashbufin_add_cookie(session, crypto->client_kex.cookie);
if (ok < 0) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: adding cookie failed");
+ goto error;
+ }
+
+ ok = server_set_kex(session);
+ if (ok == SSH_ERROR) {
goto error;
}
+#endif /* WITH_SERVER */
} else {
- len = ssh_buffer_get_data(packet,session->next_crypto->server_kex.cookie, 16);
+ len = ssh_buffer_get_data(packet, crypto->server_kex.cookie, 16);
if (len != 16) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: no cookie in packet");
goto error;
}
- ok = ssh_hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie);
+ ok = ssh_hashbufin_add_cookie(session, crypto->server_kex.cookie);
if (ok < 0) {
- ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
+ ssh_set_error(session, SSH_FATAL,
+ "ssh_packet_kexinit: adding cookie failed");
+ goto error;
+ }
+
+ ok = ssh_set_client_kex(session);
+ if (ok == SSH_ERROR) {
goto error;
}
}
@@ -404,7 +438,8 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
rc = ssh_buffer_add_ssh_string(session->in_hashbuf, str);
if (rc < 0) {
- ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer");
+ ssh_set_error(session, SSH_FATAL,
+ "Error adding string in hash buffer");
goto error;
}
@@ -417,14 +452,16 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
str = NULL;
}
- /* copy the server kex info into an array of strings */
+ /* copy the peer kex info into an array of strings */
if (server_kex) {
+#ifdef WITH_SERVER
for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->client_kex.methods[i] = strings[i];
+ crypto->client_kex.methods[i] = strings[i];
}
+#endif /* WITH_SERVER */
} else { /* client */
for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->server_kex.methods[i] = strings[i];
+ crypto->server_kex.methods[i] = strings[i];
}
}
@@ -438,30 +475,70 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
* that its value is included when computing the session ID (see
* 'make_sessionid').
*/
- if (server_kex) {
- rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
- if (rc != 1) {
- goto error;
- }
- rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows);
+ if (rc != 1) {
+ goto error;
+ }
- rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
- if (rc < 0) {
- goto error;
- }
+ rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
+
+ rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved);
+ if (rc < 0) {
+ goto error;
+ }
+ /*
+ * Remember whether 'first_kex_packet_follows' was set and the client
+ * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
+ * must be ignored on the server side.
+ * Client needs to start the Key exchange over with the correct method
+ */
+ if (first_kex_packet_follows || session->send_first_kex_follows) {
+ char **client_methods = crypto->client_kex.methods;
+ char **server_methods = crypto->server_kex.methods;
+ session->first_kex_follows_guess_wrong =
+ cmp_first_kex_algo(client_methods[SSH_KEX],
+ server_methods[SSH_KEX]) ||
+ cmp_first_kex_algo(client_methods[SSH_HOSTKEYS],
+ server_methods[SSH_HOSTKEYS]);
+ SSH_LOG(SSH_LOG_DEBUG, "The initial guess was %s.",
+ session->first_kex_follows_guess_wrong ? "wrong" : "right");
+ }
+
+ /*
+ * handle the "strict KEX" feature. If supported by peer, then set up the
+ * flag and verify packet sequence numbers.
+ */
+ if (server_kex) {
+ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX],
+ KEX_STRICT_CLIENT);
+ if (ok) {
+ SSH_LOG(SSH_LOG_DEBUG, "Client supports strict kex, enabling.");
+ session->flags |= SSH_SESSION_FLAG_KEX_STRICT;
+ }
+ } else {
+ /* client kex */
+ ok = ssh_match_group(crypto->server_kex.methods[SSH_KEX],
+ KEX_STRICT_SERVER);
+ if (ok) {
+ SSH_LOG(SSH_LOG_DEBUG, "Server supports strict kex, enabling.");
+ session->flags |= SSH_SESSION_FLAG_KEX_STRICT;
+ }
+ }
+#ifdef WITH_SERVER
+ if (server_kex) {
/*
* If client sent a ext-info-c message in the kex list, it supports
* RFC 8308 extension negotiation.
*/
- ok = ssh_match_group(session->next_crypto->client_kex.methods[SSH_KEX],
+ ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX],
KEX_EXTENSION_CLIENT);
if (ok) {
- const char *hostkeys = NULL;
+ const char *hostkeys = NULL, *wanted_hostkeys = NULL;
/* The client supports extension negotiation */
session->extensions |= SSH_EXT_NEGOTIATION;
@@ -471,14 +548,14 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
* by the client and enable the respective extensions to provide
* correct signature in the next packet if RSA is negotiated
*/
- hostkeys = session->next_crypto->client_kex.methods[SSH_HOSTKEYS];
+ hostkeys = crypto->client_kex.methods[SSH_HOSTKEYS];
+ wanted_hostkeys = session->opts.wanted_methods[SSH_HOSTKEYS];
ok = ssh_match_group(hostkeys, "rsa-sha2-512");
if (ok) {
/* Check if rsa-sha2-512 is allowed by config */
- if (session->opts.wanted_methods[SSH_HOSTKEYS] != NULL) {
- char *is_allowed =
- ssh_find_matching(session->opts.wanted_methods[SSH_HOSTKEYS],
- "rsa-sha2-512");
+ if (wanted_hostkeys != NULL) {
+ char *is_allowed = ssh_find_matching(wanted_hostkeys,
+ "rsa-sha2-512");
if (is_allowed != NULL) {
session->extensions |= SSH_EXT_SIG_RSA_SHA512;
}
@@ -488,10 +565,9 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
ok = ssh_match_group(hostkeys, "rsa-sha2-256");
if (ok) {
/* Check if rsa-sha2-256 is allowed by config */
- if (session->opts.wanted_methods[SSH_HOSTKEYS] != NULL) {
- char *is_allowed =
- ssh_find_matching(session->opts.wanted_methods[SSH_HOSTKEYS],
- "rsa-sha2-256");
+ if (wanted_hostkeys != NULL) {
+ char *is_allowed = ssh_find_matching(wanted_hostkeys,
+ "rsa-sha2-256");
if (is_allowed != NULL) {
session->extensions |= SSH_EXT_SIG_RSA_SHA256;
}
@@ -507,7 +583,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
(session->extensions & SSH_EXT_SIG_RSA_SHA512)) {
session->extensions &= ~(SSH_EXT_SIG_RSA_SHA256 | SSH_EXT_SIG_RSA_SHA512);
rsa_sig_ext = ssh_find_matching("rsa-sha2-512,rsa-sha2-256",
- session->next_crypto->client_kex.methods[SSH_HOSTKEYS]);
+ hostkeys);
if (rsa_sig_ext == NULL) {
goto error; /* should never happen */
} else if (strcmp(rsa_sig_ext, "rsa-sha2-512") == 0) {
@@ -526,24 +602,17 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit)
session->extensions & SSH_EXT_SIG_RSA_SHA256 ? "SHA256" : "",
session->extensions & SSH_EXT_SIG_RSA_SHA512 ? " SHA512" : "");
}
-
- /*
- * Remember whether 'first_kex_packet_follows' was set and the client
- * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
- * must be ignored.
- */
- if (first_kex_packet_follows) {
- session->first_kex_follows_guess_wrong =
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_KEX],
- session->next_crypto->server_kex.methods[SSH_KEX]) ||
- cmp_first_kex_algo(session->next_crypto->client_kex.methods[SSH_HOSTKEYS],
- session->next_crypto->server_kex.methods[SSH_HOSTKEYS]);
- }
}
+#endif /* WITH_SERVER */
/* Note, that his overwrites authenticated state in case of rekeying */
session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED;
- session->dh_handshake_state = DH_STATE_INIT;
+ /* if we already sent our initial key exchange packet, do not reset the
+ * DH state. We will know if we were right with our guess only in
+ * dh_handshake_state() */
+ if (session->send_first_kex_follows == false) {
+ session->dh_handshake_state = DH_STATE_INIT;
+ }
session->ssh_connection_callback(session);
return SSH_PACKET_USED;
@@ -551,7 +620,9 @@ error:
SSH_STRING_FREE(str);
for (i = 0; i < SSH_KEX_METHODS; i++) {
if (server_kex) {
+#ifdef WITH_SERVER
session->next_crypto->client_kex.methods[i] = NULL;
+#endif /* WITH_SERVER */
} else { /* client */
session->next_crypto->server_kex.methods[i] = NULL;
}
@@ -609,7 +680,7 @@ char *ssh_client_select_hostkeys(ssh_session session)
/* This removes the certificate types, unsupported for now */
wanted_without_certs = ssh_find_all_matching(HOSTKEYS, wanted);
if (wanted_without_certs == NULL) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"List of allowed host key algorithms is empty or contains only "
"unsupported algorithms");
return NULL;
@@ -662,7 +733,7 @@ char *ssh_client_select_hostkeys(ssh_session session)
fips_hostkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, new_hostkeys);
SAFE_FREE(new_hostkeys);
if (fips_hostkeys == NULL) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"None of the wanted host keys or keys in known_hosts files "
"is allowed in FIPS mode.");
return NULL;
@@ -685,11 +756,14 @@ int ssh_set_client_kex(ssh_session session)
{
struct ssh_kex_struct *client = &session->next_crypto->client_kex;
const char *wanted;
- char *kex = NULL;
- char *kex_tmp = NULL;
int ok;
int i;
- size_t kex_len, len;
+
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (client->methods[0] != NULL) {
+ return SSH_OK;
+ }
ok = ssh_get_random(client->cookie, 16, 0);
if (!ok) {
@@ -697,8 +771,6 @@ int ssh_set_client_kex(ssh_session session)
return SSH_ERROR;
}
- memset(client->methods, 0, SSH_KEX_METHODS * sizeof(char **));
-
/* Set the list of allowed algorithms in order of preference, if it hadn't
* been set yet. */
for (i = 0; i < SSH_KEX_METHODS; i++) {
@@ -734,81 +806,205 @@ int ssh_set_client_kex(ssh_session session)
return SSH_OK;
}
- /* Here we append ext-info-c to the list of kex algorithms */
- kex = client->methods[SSH_KEX];
+ ok = ssh_kex_append_extensions(session, client);
+ if (ok != SSH_OK){
+ return ok;
+ }
+
+ return SSH_OK;
+}
+
+int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex)
+{
+ char *kex = NULL;
+ char *kex_tmp = NULL;
+ size_t kex_len, len;
+
+ /* Here we append ext-info-c and kex-strict-c-v00@openssh.com for client
+ * and kex-strict-s-v00@openssh.com for server to the list of kex algorithms
+ */
+ kex = pkex->methods[SSH_KEX];
len = strlen(kex);
- if (len + strlen(KEX_EXTENSION_CLIENT) + 2 < len) {
+ if (session->server) {
+ /* Comma, nul byte */
+ kex_len = len + 1 + strlen(KEX_STRICT_SERVER) + 1;
+ } else {
+ /* Comma, comma, nul byte */
+ kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 +
+ strlen(KEX_STRICT_CLIENT) + 1;
+ }
+ if (kex_len >= MAX_PACKET_LEN) {
/* Overflow */
return SSH_ERROR;
}
- kex_len = len + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */
kex_tmp = realloc(kex, kex_len);
if (kex_tmp == NULL) {
- free(kex);
ssh_set_error_oom(session);
return SSH_ERROR;
}
- snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_EXTENSION_CLIENT);
- client->methods[SSH_KEX] = kex_tmp;
-
+ if (session->server){
+ snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_STRICT_SERVER);
+ } else {
+ snprintf(kex_tmp + len,
+ kex_len - len,
+ ",%s,%s",
+ KEX_EXTENSION_CLIENT,
+ KEX_STRICT_CLIENT);
+ }
+ pkex->methods[SSH_KEX] = kex_tmp;
return SSH_OK;
}
+static const char *ssh_find_aead_hmac(const char *cipher)
+{
+ if (cipher == NULL) {
+ return NULL;
+ } else if (strcmp(cipher, "chacha20-poly1305@openssh.com") == 0) {
+ return "aead-poly1305";
+ } else if (strcmp(cipher, "aes256-gcm@openssh.com") == 0) {
+ return "aead-gcm";
+ } else if (strcmp(cipher, "aes128-gcm@openssh.com") == 0) {
+ return "aead-gcm";
+ }
+ return NULL;
+}
+
+static enum ssh_key_exchange_e
+kex_select_kex_type(const char *kex)
+{
+ if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) {
+ return SSH_KEX_DH_GROUP1_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) {
+ return SSH_KEX_DH_GROUP14_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) {
+ return SSH_KEX_DH_GROUP14_SHA256;
+ } else if (strcmp(kex, "diffie-hellman-group16-sha512") == 0) {
+ return SSH_KEX_DH_GROUP16_SHA512;
+ } else if (strcmp(kex, "diffie-hellman-group18-sha512") == 0) {
+ return SSH_KEX_DH_GROUP18_SHA512;
+#ifdef WITH_GEX
+ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha1") == 0) {
+ return SSH_KEX_DH_GEX_SHA1;
+ } else if (strcmp(kex, "diffie-hellman-group-exchange-sha256") == 0) {
+ return SSH_KEX_DH_GEX_SHA256;
+#endif /* WITH_GEX */
+ } else if (strcmp(kex, "ecdh-sha2-nistp256") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP256;
+ } else if (strcmp(kex, "ecdh-sha2-nistp384") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP384;
+ } else if (strcmp(kex, "ecdh-sha2-nistp521") == 0) {
+ return SSH_KEX_ECDH_SHA2_NISTP521;
+ } else if (strcmp(kex, "curve25519-sha256@libssh.org") == 0) {
+ return SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
+ } else if (strcmp(kex, "curve25519-sha256") == 0) {
+ return SSH_KEX_CURVE25519_SHA256;
+ }
+ /* should not happen. We should be getting only valid names at this stage */
+ return 0;
+}
+
+
+/** @internal
+ * @brief Reverts guessed callbacks set during the dh_handshake()
+ * @param session session handle
+ * @returns void
+ */
+static void revert_kex_callbacks(ssh_session session)
+{
+ switch (session->next_crypto->kex_type) {
+ case SSH_KEX_DH_GROUP1_SHA1:
+ case SSH_KEX_DH_GROUP14_SHA1:
+ case SSH_KEX_DH_GROUP14_SHA256:
+ case SSH_KEX_DH_GROUP16_SHA512:
+ case SSH_KEX_DH_GROUP18_SHA512:
+ ssh_client_dh_remove_callbacks(session);
+ break;
+#ifdef WITH_GEX
+ case SSH_KEX_DH_GEX_SHA1:
+ case SSH_KEX_DH_GEX_SHA256:
+ ssh_client_dhgex_remove_callbacks(session);
+ break;
+#endif /* WITH_GEX */
+#ifdef HAVE_ECDH
+ case SSH_KEX_ECDH_SHA2_NISTP256:
+ case SSH_KEX_ECDH_SHA2_NISTP384:
+ case SSH_KEX_ECDH_SHA2_NISTP521:
+ ssh_client_ecdh_remove_callbacks(session);
+ break;
+#endif
+#ifdef HAVE_CURVE25519
+ case SSH_KEX_CURVE25519_SHA256:
+ case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
+ ssh_client_curve25519_remove_callbacks(session);
+ break;
+#endif
+ }
+}
+
/** @brief Select the different methods on basis of client's and
* server's kex messages, and watches out if a match is possible.
*/
-int ssh_kex_select_methods (ssh_session session){
- struct ssh_kex_struct *server = &session->next_crypto->server_kex;
- struct ssh_kex_struct *client = &session->next_crypto->client_kex;
+int ssh_kex_select_methods (ssh_session session)
+{
+ struct ssh_crypto_struct *crypto = session->next_crypto;
+ struct ssh_kex_struct *server = &crypto->server_kex;
+ struct ssh_kex_struct *client = &crypto->client_kex;
char *ext_start = NULL;
+ const char *aead_hmac = NULL;
+ enum ssh_key_exchange_e kex_type;
int i;
- /* Here we should drop the ext-info-c from the list so we avoid matching.
+ /* Here we should drop the extensions from the list so we avoid matching.
* it. We added it to the end, so we can just truncate the string here */
- ext_start = strstr(client->methods[SSH_KEX], ","KEX_EXTENSION_CLIENT);
- if (ext_start != NULL) {
- ext_start[0] = '\0';
+ if (session->client) {
+ ext_start = strstr(client->methods[SSH_KEX], "," KEX_EXTENSION_CLIENT);
+ if (ext_start != NULL) {
+ ext_start[0] = '\0';
+ }
+ }
+ if (session->server) {
+ ext_start = strstr(server->methods[SSH_KEX], "," KEX_STRICT_SERVER);
+ if (ext_start != NULL) {
+ ext_start[0] = '\0';
+ }
}
for (i = 0; i < SSH_KEX_METHODS; i++) {
- session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]);
- if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){
- ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]",
- ssh_kex_descriptions[i],server->methods[i],client->methods[i]);
+ crypto->kex_methods[i] = ssh_find_matching(server->methods[i],
+ client->methods[i]);
+
+ if (i == SSH_MAC_C_S || i == SSH_MAC_S_C) {
+ aead_hmac = ssh_find_aead_hmac(crypto->kex_methods[i - 2]);
+ if (aead_hmac) {
+ free(crypto->kex_methods[i]);
+ crypto->kex_methods[i] = strdup(aead_hmac);
+ }
+ }
+ if (crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S) {
+ ssh_set_error(session, SSH_FATAL,
+ "kex error : no match for method %s: server [%s], "
+ "client [%s]", ssh_kex_descriptions[i],
+ server->methods[i], client->methods[i]);
return SSH_ERROR;
- } else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) {
+ } else if ((i >= SSH_LANG_C_S) && (crypto->kex_methods[i] == NULL)) {
/* we can safely do that for languages */
- session->next_crypto->kex_methods[i] = strdup("");
+ crypto->kex_methods[i] = strdup("");
}
}
- if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA256;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group16-sha512") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP16_SHA512;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group18-sha512") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GROUP18_SHA512;
-#ifdef WITH_GEX
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha1") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA1;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_DH_GEX_SHA256;
-#endif /* WITH_GEX */
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp384") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP384;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp521") == 0){
- session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP521;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG;
- } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256") == 0){
- session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256;
- }
- SSH_LOG(SSH_LOG_INFO, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
+
+ /* We can not set this value directly as the old value is needed to revert
+ * callbacks if we are client */
+ kex_type = kex_select_kex_type(crypto->kex_methods[SSH_KEX]);
+ if (session->client && session->first_kex_follows_guess_wrong) {
+ SSH_LOG(SSH_LOG_DEBUG, "Our guess was wrong. Restarting the KEX");
+ /* We need to remove the wrong callbacks and start kex again */
+ revert_kex_callbacks(session);
+ session->dh_handshake_state = DH_STATE_INIT;
+ session->first_kex_follows_guess_wrong = false;
+ }
+ crypto->kex_type = kex_type;
+
+ SSH_LOG(SSH_LOG_DEBUG, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
session->next_crypto->kex_methods[SSH_KEX],
session->next_crypto->kex_methods[SSH_HOSTKEYS],
session->next_crypto->kex_methods[SSH_CRYPT_C_S],
@@ -825,62 +1021,116 @@ int ssh_kex_select_methods (ssh_session session){
/* this function only sends the predefined set of kex methods */
-int ssh_send_kex(ssh_session session, int server_kex) {
- struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
- &session->next_crypto->client_kex);
- ssh_string str = NULL;
- int i;
- int rc;
-
- rc = ssh_buffer_pack(session->out_buffer,
- "bP",
- SSH2_MSG_KEXINIT,
- 16,
- kex->cookie); /* cookie */
- if (rc != SSH_OK)
- goto error;
- if (ssh_hashbufout_add_cookie(session) < 0) {
- goto error;
- }
+int ssh_send_kex(ssh_session session)
+{
+ struct ssh_kex_struct *kex = (session->server ?
+ &session->next_crypto->server_kex :
+ &session->next_crypto->client_kex);
+ ssh_string str = NULL;
+ int i;
+ int rc;
+ int first_kex_packet_follows = 0;
+
+ /* Only client can initiate the handshake methods we implement. If we
+ * already received the peer mechanisms, there is no point in guessing */
+ if (session->client &&
+ session->session_state != SSH_SESSION_STATE_KEXINIT_RECEIVED &&
+ session->send_first_kex_follows) {
+ first_kex_packet_follows = 1;
+ }
+
+ SSH_LOG(SSH_LOG_TRACE,
+ "Sending KEXINIT packet, first_kex_packet_follows = %d",
+ first_kex_packet_follows);
+
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bP",
+ SSH2_MSG_KEXINIT,
+ 16,
+ kex->cookie); /* cookie */
+ if (rc != SSH_OK)
+ goto error;
+ if (ssh_hashbufout_add_cookie(session) < 0) {
+ goto error;
+ }
+
+ ssh_list_kex(kex);
- ssh_list_kex(kex);
+ for (i = 0; i < SSH_KEX_METHODS; i++) {
+ str = ssh_string_from_char(kex->methods[i]);
+ if (str == NULL) {
+ goto error;
+ }
- for (i = 0; i < SSH_KEX_METHODS; i++) {
- str = ssh_string_from_char(kex->methods[i]);
- if (str == NULL) {
- goto error;
+ rc = ssh_buffer_add_ssh_string(session->out_hashbuf, str);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_ssh_string(session->out_buffer, str);
+ if (rc < 0) {
+ goto error;
+ }
+ SSH_STRING_FREE(str);
+ str = NULL;
}
- if (ssh_buffer_add_ssh_string(session->out_hashbuf, str) < 0) {
- goto error;
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bd",
+ first_kex_packet_follows,
+ 0);
+ if (rc != SSH_OK) {
+ goto error;
}
- if (ssh_buffer_add_ssh_string(session->out_buffer, str) < 0) {
- goto error;
+
+ /* Prepare also the first_kex_packet_follows and reserved to 0 */
+ rc = ssh_buffer_add_u8(session->out_hashbuf, first_kex_packet_follows);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = ssh_buffer_add_u32(session->out_hashbuf, 0);
+ if (rc < 0) {
+ goto error;
}
- SSH_STRING_FREE(str);
- str = NULL;
- }
- rc = ssh_buffer_pack(session->out_buffer,
- "bd",
- 0,
- 0);
- if (rc != SSH_OK) {
- goto error;
- }
+ rc = ssh_packet_send(session);
+ if (rc == SSH_ERROR) {
+ return -1;
+ }
- if (ssh_packet_send(session) == SSH_ERROR) {
- return -1;
- }
+ session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT;
+ SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
+
+ /* If we indicated that we are sending the guessed key exchange packet,
+ * do it now. The packet is simple, but we need to do some preparations */
+ if (first_kex_packet_follows == 1) {
+ char *list = kex->methods[SSH_KEX];
+ char *colon = strchr(list, ',');
+ size_t kex_name_len = colon ? (size_t)(colon - list) : strlen(list);
+ char *kex_name = calloc(kex_name_len + 1, 1);
+ if (kex_name == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+ snprintf(kex_name, kex_name_len + 1, "%.*s", (int)kex_name_len, list);
+ SSH_LOG(SSH_LOG_TRACE, "Sending the first kex packet for %s", kex_name);
+
+ session->next_crypto->kex_type = kex_select_kex_type(kex_name);
+ free(kex_name);
+
+ /* run the first step of the DH handshake */
+ session->dh_handshake_state = DH_STATE_INIT;
+ if (dh_handshake(session) == SSH_ERROR) {
+ goto error;
+ }
+ }
+ return 0;
- SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent");
- return 0;
error:
- ssh_buffer_reinit(session->out_buffer);
- ssh_buffer_reinit(session->out_hashbuf);
- SSH_STRING_FREE(str);
+ ssh_buffer_reinit(session->out_buffer);
+ ssh_buffer_reinit(session->out_hashbuf);
+ SSH_STRING_FREE(str);
- return -1;
+ return -1;
}
/*
@@ -923,7 +1173,7 @@ int ssh_send_rekex(ssh_session session)
}
session->dh_handshake_state = DH_STATE_INIT;
- rc = ssh_send_kex(session, session->server);
+ rc = ssh_send_kex(session);
if (rc < 0) {
SSH_LOG(SSH_LOG_PACKET, "Failed to send kex");
return rc;
@@ -948,13 +1198,13 @@ char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list)
/**
* @internal
*
- * @brief Return a new allocated string containing only the FIPS allowed
+ * @brief Return a newly allocated string containing only the FIPS allowed
* algorithms from the list.
*
* @param[in] algo The type of the methods to filter
* @param[in] list The list to be filtered
*
- * @return A new allocated list containing only the FIPS allowed algorithms from
+ * @return A newly allocated list containing only the FIPS allowed algorithms from
* the list; NULL in case of error.
*/
char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list)
@@ -966,6 +1216,111 @@ char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list)
return ssh_find_all_matching(fips_methods[algo], list);
}
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms plus the algorithms specified in list. If the system
+ * runs in fips mode, this will add only fips approved algorithms.
+ * Empty list will cause error.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be appended
+ *
+ * @return A newly allocated list containing the default algorithms and the
+ * algorithms in list at the end; NULL in case of error.
+ */
+char *ssh_add_to_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ char *tmp = NULL, *ret = NULL;
+
+ if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') {
+ return NULL;
+ }
+
+ if (ssh_fips_mode()) {
+ tmp = ssh_append_without_duplicates(fips_methods[algo], list);
+ ret = ssh_find_all_matching(fips_methods[algo], tmp);
+ } else {
+ tmp = ssh_append_without_duplicates(default_methods[algo], list);
+ ret = ssh_find_all_matching(supported_methods[algo], tmp);
+ }
+
+ free(tmp);
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms excluding the algorithms specified in list. If the system
+ * runs in fips mode, this will remove from the fips_methods list.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be exclude
+ *
+ * @return A newly allocated list containing the default algorithms without the
+ * algorithms in list; NULL in case of error.
+ */
+char *ssh_remove_from_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ if (algo > SSH_LANG_S_C) {
+ return NULL;
+ }
+
+ if (list == NULL || list[0] == '\0') {
+ if (ssh_fips_mode()) {
+ return strdup(fips_methods[algo]);
+ } else {
+ return strdup(default_methods[algo]);
+ }
+ }
+
+ if (ssh_fips_mode()) {
+ return ssh_remove_all_matching(fips_methods[algo], list);
+ } else {
+ return ssh_remove_all_matching(default_methods[algo], list);
+ }
+}
+
+/**
+ * @internal
+ *
+ * @brief Return a newly allocated string containing the default
+ * algorithms with prioritized algorithms specified in list. If the
+ * algorithms are present in the default list they get prioritized, if not
+ * they are added to the front of the default list. If the system
+ * runs in fips mode, this will work with the fips_methods list.
+ * Empty list will cause error.
+ *
+ * @param[in] algo The type of the methods to filter
+ * @param[in] list The list to be pushed to priority
+ *
+ * @return A newly allocated list containing the default algorithms prioritized
+ * with the algorithms in list at the beginning of the list; NULL in case
+ * of error.
+ */
+char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list)
+{
+ char *ret = NULL, *tmp = NULL;
+
+ if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') {
+ return NULL;
+ }
+
+ if (ssh_fips_mode()) {
+ tmp = ssh_prefix_without_duplicates(fips_methods[algo], list);
+ ret = ssh_find_all_matching(fips_methods[algo], tmp);
+ } else {
+ tmp = ssh_prefix_without_duplicates(default_methods[algo], list);
+ ret = ssh_find_all_matching(supported_methods[algo], tmp);
+ }
+
+ free(tmp);
+ return ret;
+}
+
int ssh_make_sessionid(ssh_session session)
{
ssh_string num = NULL;
@@ -973,10 +1328,18 @@ int ssh_make_sessionid(ssh_session session)
ssh_buffer client_hash = NULL;
ssh_buffer buf = NULL;
ssh_string server_pubkey_blob = NULL;
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum client_pubkey, server_pubkey;
+#else
+ bignum client_pubkey = NULL, server_pubkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
#ifdef WITH_GEX
+#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum modulus, generator;
-#endif
+#else
+ bignum modulus = NULL, generator = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* WITH_GEX */
int rc = SSH_ERROR;
buf = ssh_buffer_new();
@@ -1000,33 +1363,6 @@ int ssh_make_sessionid(ssh_session session)
client_hash = session->in_hashbuf;
}
- /*
- * Handle the two final fields for the KEXINIT message (RFC 4253 7.1):
- *
- * boolean first_kex_packet_follows
- * uint32 0 (reserved for future extension)
- */
- rc = ssh_buffer_add_u8(server_hash, 0);
- if (rc < 0) {
- goto error;
- }
- rc = ssh_buffer_add_u32(server_hash, 0);
- if (rc < 0) {
- goto error;
- }
-
- /* These fields are handled for the server case in ssh_packet_kexinit. */
- if (session->client) {
- rc = ssh_buffer_add_u8(client_hash, 0);
- if (rc < 0) {
- goto error;
- }
- rc = ssh_buffer_add_u32(client_hash, 0);
- if (rc < 0) {
- goto error;
- }
- }
-
rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
if (rc != SSH_OK) {
goto error;
@@ -1042,7 +1378,7 @@ int ssh_make_sessionid(ssh_session session)
ssh_buffer_get(server_hash),
server_pubkey_blob);
SSH_STRING_FREE(server_pubkey_blob);
- if(rc != SSH_OK){
+ if (rc != SSH_OK){
goto error;
}
@@ -1069,6 +1405,10 @@ int ssh_make_sessionid(ssh_session session)
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(client_pubkey);
+ bignum_safe_free(server_pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
break;
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
@@ -1100,6 +1440,10 @@ int ssh_make_sessionid(ssh_session session)
if (rc != SSH_OK) {
goto error;
}
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(modulus);
+ bignum_safe_free(generator);
+#endif /* OPENSSL_VERSION_NUMBER */
break;
#endif /* WITH_GEX */
#ifdef HAVE_ECDH
@@ -1108,7 +1452,7 @@ int ssh_make_sessionid(ssh_session session)
case SSH_KEX_ECDH_SHA2_NISTP521:
if (session->next_crypto->ecdh_client_pubkey == NULL ||
session->next_crypto->ecdh_server_pubkey == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing");
+ SSH_LOG(SSH_LOG_TRACE, "ECDH parameter missing");
goto error;
}
rc = ssh_buffer_pack(buf,
@@ -1119,7 +1463,7 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
break;
-#endif
+#endif /* HAVE_ECDH */
#ifdef HAVE_CURVE25519
case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
@@ -1134,7 +1478,7 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
break;
-#endif
+#endif /* HAVE_CURVE25519 */
}
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
if (rc != SSH_OK) {
@@ -1216,12 +1560,14 @@ int ssh_make_sessionid(ssh_session session)
}
memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash,
session->next_crypto->digest_len);
+ /* Initial length is the same as secret hash */
+ session->next_crypto->session_id_len = session->next_crypto->digest_len;
}
#ifdef DEBUG_CRYPTO
- printf("Session hash: \n");
+ SSH_LOG(SSH_LOG_DEBUG, "Session hash: \n");
ssh_log_hexdump("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len);
- ssh_log_hexdump("session id", session->next_crypto->session_id, session->next_crypto->digest_len);
-#endif
+ ssh_log_hexdump("session id", session->next_crypto->session_id, session->next_crypto->session_id_len);
+#endif /* DEBUG_CRYPTO */
rc = SSH_OK;
error:
@@ -1233,6 +1579,10 @@ error:
session->out_hashbuf = NULL;
SSH_STRING_FREE(num);
+#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(client_pubkey);
+ bignum_safe_free(server_pubkey);
+#endif /* OPENSSL_VERSION_NUMBER */
return rc;
}
@@ -1417,7 +1767,7 @@ int ssh_generate_session_keys(ssh_session session)
intkey_cli_to_srv_len);
ssh_log_hexdump("Server to Client Integrity Key", intkey_srv_to_cli,
intkey_srv_to_cli_len);
-#endif
+#endif /* DEBUG_CRYPTO */
rc = 0;
error:
diff --git a/src/known_hosts.c b/src/known_hosts.c
index ec6da308..a1d9a432 100644
--- a/src/known_hosts.c
+++ b/src/known_hosts.c
@@ -45,6 +45,10 @@
# include <arpa/inet.h>
#endif
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 4096
+#endif
+
/**
* @addtogroup libssh_session
*
@@ -61,12 +65,12 @@
* @param[out] file A pointer to the known host file. Could be pointing to
* NULL at start.
*
- * @param[in] filename The file name of the known host file.
+ * @param[in] filename The filename of the known host file.
*
* @param[out] found_type A pointer to a string to be set with the found key
* type.
*
- * @returns The found_type type of key (ie "dsa","ssh-rsa"). Don't
+ * @returns The found_type type of key (ie "ssh-rsa"). Don't
* free that value. NULL if no match was found or the file
* was not found.
*/
@@ -74,7 +78,7 @@ static struct ssh_tokens_st *ssh_get_knownhost_line(FILE **file,
const char *filename,
const char **found_type)
{
- char buffer[4096] = {0};
+ char buffer[MAX_LINE_SIZE] = {0};
char *ptr;
struct ssh_tokens_st *tokens;
@@ -148,7 +152,7 @@ static int check_public_key(ssh_session session, char **tokens) {
char *pubkey_64;
int rc;
- /* ssh-dss or ssh-rsa */
+ /* ssh-rsa, ssh-ed25519, .. */
pubkey_64 = tokens[2];
pubkey_buffer = base64_to_bin(pubkey_64);
@@ -206,8 +210,8 @@ static int match_hashed_host(const char *host, const char *sourcehash)
HMACCTX mac;
char *source;
char *b64hash;
- int match;
- unsigned int size;
+ int match, rc;
+ size_t size;
if (strncmp(sourcehash, "|1|", 3) != 0) {
return 0;
@@ -252,8 +256,20 @@ static int match_hashed_host(const char *host, const char *sourcehash)
return 0;
}
size = sizeof(buffer);
- hmac_update(mac, host, strlen(host));
- hmac_final(mac, buffer, &size);
+ rc = hmac_update(mac, host, strlen(host));
+ if (rc != 1) {
+ ssh_buffer_free(salt);
+ ssh_buffer_free(hash);
+
+ return 0;
+ }
+ rc = hmac_final(mac, buffer, &size);
+ if (rc != 1) {
+ ssh_buffer_free(salt);
+ ssh_buffer_free(hash);
+
+ return 0;
+ }
if (size == ssh_buffer_get_len(hash) &&
memcmp(buffer, ssh_buffer_get(hash), size) == 0) {
@@ -431,7 +447,6 @@ char * ssh_dump_knownhost(ssh_session session) {
ssh_key server_pubkey = NULL;
char *host;
char *hostport;
- size_t len = 4096;
char *buffer;
char *b64_key;
int rc;
@@ -467,7 +482,7 @@ char * ssh_dump_knownhost(ssh_session session) {
return NULL;
}
- buffer = calloc (1, 4096);
+ buffer = calloc (1, MAX_LINE_SIZE);
if (!buffer) {
SAFE_FREE(host);
return NULL;
@@ -480,7 +495,7 @@ char * ssh_dump_knownhost(ssh_session session) {
return NULL;
}
- snprintf(buffer, len,
+ snprintf(buffer, MAX_LINE_SIZE,
"%s %s %s\n",
host,
server_pubkey->type_c,
@@ -513,10 +528,12 @@ int ssh_write_knownhost(ssh_session session)
errno = 0;
file = fopen(session->opts.knownhosts, "a");
if (file == NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (errno == ENOENT) {
dir = ssh_dirname(session->opts.knownhosts);
if (dir == NULL) {
- ssh_set_error(session, SSH_FATAL, "%s", strerror(errno));
+ ssh_set_error(session, SSH_FATAL,
+ "%s", ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -524,7 +541,7 @@ int ssh_write_knownhost(ssh_session session)
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Cannot create %s directory: %s",
- dir, strerror(errno));
+ dir, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(dir);
return SSH_ERROR;
}
@@ -536,13 +553,15 @@ int ssh_write_knownhost(ssh_session session)
ssh_set_error(session, SSH_FATAL,
"Couldn't open known_hosts file %s"
" for appending: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
} else {
ssh_set_error(session, SSH_FATAL,
"Couldn't open known_hosts file %s for appending: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
}
diff --git a/src/knownhosts.c b/src/knownhosts.c
index fed75f90..c073b266 100644
--- a/src/knownhosts.c
+++ b/src/knownhosts.c
@@ -44,6 +44,10 @@
#include "libssh/knownhosts.h"
#include "libssh/token.h"
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 8192
+#endif
+
/**
* @addtogroup libssh_session
*
@@ -54,8 +58,9 @@ static int hash_hostname(const char *name,
unsigned char *salt,
unsigned int salt_size,
unsigned char **hash,
- unsigned int *hash_size)
+ size_t *hash_size)
{
+ int rc;
HMACCTX mac_ctx;
mac_ctx = hmac_init(salt, salt_size, SSH_HMAC_SHA1);
@@ -63,8 +68,13 @@ static int hash_hostname(const char *name,
return SSH_ERROR;
}
- hmac_update(mac_ctx, name, strlen(name));
- hmac_final(mac_ctx, *hash, hash_size);
+ rc = hmac_update(mac_ctx, name, strlen(name));
+ if (rc != 1)
+ return SSH_ERROR;
+
+ rc = hmac_final(mac_ctx, *hash, hash_size);
+ if (rc != 1)
+ return SSH_ERROR;
return SSH_OK;
}
@@ -77,7 +87,7 @@ static int match_hashed_hostname(const char *host, const char *hashed_host)
ssh_buffer hash = NULL;
unsigned char hashed_buf[256] = {0};
unsigned char *hashed_buf_ptr = hashed_buf;
- unsigned int hashed_buf_size = sizeof(hashed_buf);
+ size_t hashed_buf_size = sizeof(hashed_buf);
int cmp;
int rc;
int match = 0;
@@ -216,7 +226,7 @@ static int ssh_known_hosts_read_entries(const char *match,
const char *filename,
struct ssh_list **entries)
{
- char line[8192];
+ char line[MAX_LINE_SIZE];
size_t lineno = 0;
size_t len = 0;
FILE *fp;
@@ -224,8 +234,9 @@ static int ssh_known_hosts_read_entries(const char *match,
fp = fopen(filename, "r");
if (fp == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Failed to open the known_hosts file '%s': %s",
- filename, strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
/* The missing file is not an error here */
return SSH_OK;
}
@@ -372,6 +383,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
list = ssh_list_new();
if (list == NULL) {
+ ssh_set_error_oom(session);
SAFE_FREE(host_port);
return NULL;
}
@@ -468,24 +480,33 @@ static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e typ
return "rsa-sha2-512,rsa-sha2-256,ssh-rsa";
case SSH_KEYTYPE_ED25519:
return "ssh-ed25519";
-#ifdef HAVE_DSA
- case SSH_KEYTYPE_DSS:
- return "ssh-dss";
-#endif
-#ifdef HAVE_ECDH
+ case SSH_KEYTYPE_SK_ED25519:
+ return "sk-ssh-ed25519@openssh.com";
+#ifdef HAVE_ECC
case SSH_KEYTYPE_ECDSA_P256:
return "ecdsa-sha2-nistp256";
case SSH_KEYTYPE_ECDSA_P384:
return "ecdsa-sha2-nistp384";
case SSH_KEYTYPE_ECDSA_P521:
return "ecdsa-sha2-nistp521";
+ case SSH_KEYTYPE_SK_ECDSA:
+ return "sk-ecdsa-sha2-nistp256@openssh.com";
+#else
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P521:
+ SSH_LOG(SSH_LOG_WARN, "ECDSA keys are not supported by this build");
+ break;
#endif
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "The given type %d is not a base private key type "
- "or is unsupported", type);
- return NULL;
+ SSH_LOG(SSH_LOG_TRACE,
+ "The given type %d is not a base private key type "
+ "or is unsupported",
+ type);
}
+
+ return NULL;
}
/**
@@ -567,6 +588,8 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
algo = ssh_known_host_sigs_from_hostkey_type(entry->publickey->type);
if (algo == NULL) {
+ ssh_knownhosts_entry_free(entry);
+ ssh_list_remove(entry_list, it);
continue;
}
@@ -595,7 +618,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
/**
* @brief Parse a line from a known_hosts entry into a structure
*
- * This parses an known_hosts entry into a structure with the key in a libssh
+ * This parses a known_hosts entry into a structure with the key in a libssh
* consumeable form. You can use the PKI key function to further work with it.
*
* @param[in] hostname The hostname to match the line to
@@ -603,7 +626,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
* @param[in] line The line to compare and parse if we have a hostname
* match.
*
- * @param[in] entry A pointer to store the the allocated known_hosts
+ * @param[in] entry A pointer to store the allocated known_hosts
* entry structure. The user needs to free the memory
* using SSH_KNOWNHOSTS_ENTRY_FREE().
*
@@ -616,6 +639,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
struct ssh_knownhosts_entry *e = NULL;
char *known_host = NULL;
char *p;
+ char *save_tok = NULL;
enum ssh_keytypes_e key_type;
int match = 0;
int rc = SSH_OK;
@@ -626,7 +650,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
}
/* match pattern for hostname or hashed hostname */
- p = strtok(known_host, " ");
+ p = strtok_r(known_host, " ", &save_tok);
if (p == NULL ) {
free(known_host);
return SSH_ERROR;
@@ -647,14 +671,16 @@ int ssh_known_hosts_parse_line(const char *hostname,
match = match_hashed_hostname(hostname, p);
}
- for (q = strtok(p, ",");
+ save_tok = NULL;
+
+ for (q = strtok_r(p, ",", &save_tok);
q != NULL;
- q = strtok(NULL, ",")) {
+ q = strtok_r(NULL, ",", &save_tok)) {
int cmp;
if (q[0] == '[' && hostname[0] != '[') {
/* Corner case: We have standard port so we do not have
- * hostname in square braces. But the patern is enclosed
+ * hostname in square braces. But the pattern is enclosed
* in braces with, possibly standard or wildcard, port.
* We need to test against [host]:port pair here.
*/
@@ -697,7 +723,9 @@ int ssh_known_hosts_parse_line(const char *hostname,
goto out;
}
- p = strtok(known_host, " ");
+ save_tok = NULL;
+
+ p = strtok_r(known_host, " ", &save_tok);
if (p == NULL ) {
rc = SSH_ERROR;
goto out;
@@ -710,7 +738,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
}
/* pubkey type */
- p = strtok(NULL, " ");
+ p = strtok_r(NULL, " ", &save_tok);
if (p == NULL) {
rc = SSH_ERROR;
goto out;
@@ -718,13 +746,13 @@ int ssh_known_hosts_parse_line(const char *hostname,
key_type = ssh_key_type_from_name(p);
if (key_type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "key type '%s' unknown!", p);
+ SSH_LOG(SSH_LOG_TRACE, "key type '%s' unknown!", p);
rc = SSH_ERROR;
goto out;
}
/* public key */
- p = strtok(NULL, " ");
+ p = strtok_r(NULL, " ", &save_tok);
if (p == NULL) {
rc = SSH_ERROR;
goto out;
@@ -734,7 +762,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
key_type,
&e->publickey);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to parse %s key for entry: %s!",
ssh_key_type_to_char(key_type),
e->unparsed);
@@ -742,7 +770,7 @@ int ssh_known_hosts_parse_line(const char *hostname,
}
/* comment */
- p = strtok(NULL, " ");
+ p = strtok_r(NULL, " ", &save_tok);
if (p != NULL) {
p = strstr(line, p);
if (p != NULL) {
@@ -765,12 +793,12 @@ out:
}
/**
- * @brief Check if the set hostname and port matches an entry in known_hosts.
+ * @brief Check if the set hostname and port match an entry in known_hosts.
*
- * This check if the set hostname and port has an entry in the known_hosts file.
+ * This check if the set hostname and port have an entry in the known_hosts file.
* You need to set at least the hostname using ssh_options_set().
*
- * @param[in] session The session with with the values set to check.
+ * @param[in] session The session with the values set to check.
*
* @return A ssh_known_hosts_e return value.
*/
@@ -805,7 +833,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
if (session->opts.knownhosts != NULL) {
known_hosts_found = ssh_file_readaccess_ok(session->opts.knownhosts);
if (!known_hosts_found) {
- SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
+ SSH_LOG(SSH_LOG_TRACE, "Cannot access file %s",
session->opts.knownhosts);
}
}
@@ -814,7 +842,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
global_known_hosts_found =
ssh_file_readaccess_ok(session->opts.global_knownhosts);
if (!global_known_hosts_found) {
- SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
+ SSH_LOG(SSH_LOG_TRACE, "Cannot access file %s",
session->opts.global_knownhosts);
}
}
@@ -883,18 +911,18 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
*
* @param[in] session The session with information to export.
*
- * @param[in] pentry_string A pointer to a string to store the alloocated
+ * @param[in] pentry_string A pointer to a string to store the allocated
* line of the entry. The user must free it using
* ssh_string_free_char().
*
- * @return SSH_OK on succcess, SSH_ERROR otherwise.
+ * @return SSH_OK on success, SSH_ERROR otherwise.
*/
int ssh_session_export_known_hosts_entry(ssh_session session,
char **pentry_string)
{
ssh_key server_pubkey = NULL;
char *host = NULL;
- char entry_buf[4096] = {0};
+ char entry_buf[MAX_LINE_SIZE] = {0};
char *b64_key = NULL;
int rc;
@@ -952,7 +980,7 @@ int ssh_session_export_known_hosts_entry(ssh_session session,
}
/**
- * @brief Add the current connected server to the user known_hosts file.
+ * @brief Adds the currently connected server to the user known_hosts file.
*
* This adds the currently connected server to the known_hosts file by
* appending a new line at the end. The global known_hosts file is considered
@@ -970,6 +998,7 @@ int ssh_session_update_known_hosts(ssh_session session)
size_t nwritten;
size_t len;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (session->opts.knownhosts == NULL) {
rc = ssh_options_apply(session);
@@ -985,7 +1014,8 @@ int ssh_session_update_known_hosts(ssh_session session)
if (errno == ENOENT) {
dir = ssh_dirname(session->opts.knownhosts);
if (dir == NULL) {
- ssh_set_error(session, SSH_FATAL, "%s", strerror(errno));
+ ssh_set_error(session, SSH_FATAL, "%s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -993,7 +1023,8 @@ int ssh_session_update_known_hosts(ssh_session session)
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"Cannot create %s directory: %s",
- dir, strerror(errno));
+ dir,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(dir);
return SSH_ERROR;
}
@@ -1005,7 +1036,8 @@ int ssh_session_update_known_hosts(ssh_session session)
ssh_set_error(session, SSH_FATAL,
"Couldn't open known_hosts file %s"
" for appending: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
} else {
@@ -1028,7 +1060,8 @@ int ssh_session_update_known_hosts(ssh_session session)
if (nwritten != len || ferror(fp)) {
ssh_set_error(session, SSH_FATAL,
"Couldn't append to known_hosts file %s: %s",
- session->opts.knownhosts, strerror(errno));
+ session->opts.knownhosts,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
fclose(fp);
return SSH_ERROR;
}
@@ -1103,7 +1136,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
}
/**
- * @brief Get the known_hosts entry for the current connected session.
+ * @brief Get the known_hosts entry for the currently connected session.
*
* @param[in] session The session to validate.
*
@@ -1122,7 +1155,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
* SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
* host is thus unknown. File will be
* created if host key is accepted.\n
- * SSH_KNOWN_HOSTS_ERROR: There had been an eror checking the host.
+ * SSH_KNOWN_HOSTS_ERROR: There had been an error checking the host.
*
* @see ssh_knownhosts_entry_free()
*/
@@ -1191,7 +1224,7 @@ ssh_session_get_known_hosts_entry(ssh_session session,
* SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
* host is thus unknown. File will be
* created if host key is accepted.\n
- * SSH_KNOWN_HOSTS_ERROR: There had been an eror checking the host.
+ * SSH_KNOWN_HOSTS_ERROR: There had been an error checking the host.
*
* @see ssh_knownhosts_entry_free()
*/
diff --git a/src/legacy.c b/src/legacy.c
index 8ae3d920..72ff7f36 100644
--- a/src/legacy.c
+++ b/src/legacy.c
@@ -20,7 +20,7 @@
*/
/** functions in that file are wrappers to the newly named functions. All
- * of them are depreciated, but these wrapper will avoid breaking backward
+ * of them are depreciated, but these wrappers will avoid breaking backward
* compatibility
*/
@@ -83,12 +83,18 @@ int ssh_userauth_pubkey(ssh_session session,
key->type = privatekey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PRIVATE|SSH_KEY_FLAG_PUBLIC;
- key->dsa = privatekey->dsa_priv;
+#ifndef HAVE_LIBCRYPTO
key->rsa = privatekey->rsa_priv;
+#else
+ key->key = privatekey->key_priv;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_userauth_publickey(session, username, key);
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return rc;
@@ -164,7 +170,7 @@ int channel_change_pty_size(ssh_channel channel,int cols,int rows){
}
ssh_channel channel_forward_accept(ssh_session session, int timeout_ms){
- return ssh_channel_accept_forward(session, timeout_ms, NULL);
+ return ssh_channel_open_forward_port(session, timeout_ms, NULL, NULL, NULL);
}
int channel_close(ssh_channel channel){
@@ -350,22 +356,15 @@ void publickey_free(ssh_public_key key) {
}
switch(key->type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_LIBGCRYPT
- gcry_sexp_release(key->dsa_pub);
-#elif defined HAVE_LIBCRYPTO
- DSA_free(key->dsa_pub);
-#endif
- break;
case SSH_KEYTYPE_RSA:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(key->rsa_pub);
#elif defined HAVE_LIBCRYPTO
- RSA_free(key->rsa_pub);
+ EVP_PKEY_free(key->key_pub);
#elif defined HAVE_LIBMBEDCRYPTO
mbedtls_pk_free(key->rsa_pub);
SAFE_FREE(key->rsa_pub);
-#endif
+#endif /* HAVE_LIBGCRYPT */
break;
default:
break;
@@ -387,12 +386,18 @@ ssh_public_key publickey_from_privatekey(ssh_private_key prv) {
privkey->type = prv->type;
privkey->type_c = ssh_key_type_to_char(privkey->type);
privkey->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- privkey->dsa = prv->dsa_priv;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa = prv->rsa_priv;
+#else
+ privkey->key = prv->key_priv;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
- privkey->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa = NULL;
+#else
+ privkey->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(privkey);
if (rc < 0) {
return NULL;
@@ -438,11 +443,15 @@ ssh_private_key privatekey_from_file(ssh_session session,
}
privkey->type = key->type;
- privkey->dsa_priv = key->dsa;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa_priv = key->rsa;
- key->dsa = NULL;
key->rsa = NULL;
+#else
+ privkey->key_priv = key->key;
+
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
@@ -461,15 +470,13 @@ void privatekey_free(ssh_private_key prv) {
}
#ifdef HAVE_LIBGCRYPT
- gcry_sexp_release(prv->dsa_priv);
gcry_sexp_release(prv->rsa_priv);
#elif defined HAVE_LIBCRYPTO
- DSA_free(prv->dsa_priv);
- RSA_free(prv->rsa_priv);
+ EVP_PKEY_free(prv->key_priv);
#elif defined HAVE_LIBMBEDCRYPTO
mbedtls_pk_free(prv->rsa_priv);
SAFE_FREE(prv->rsa_priv);
-#endif
+#endif /* HAVE_LIBGCRYPT */
memset(prv, 0, sizeof(struct ssh_private_key_struct));
SAFE_FREE(prv);
}
@@ -530,10 +537,13 @@ ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s) {
pubkey->type = key->type;
pubkey->type_c = key->type_c;
- pubkey->dsa_pub = key->dsa;
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
pubkey->rsa_pub = key->rsa;
key->rsa = NULL;
+#else
+ pubkey->key_pub = key->key;
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
@@ -557,16 +567,22 @@ ssh_string publickey_to_string(ssh_public_key pubkey) {
key->type = pubkey->type;
key->type_c = pubkey->type_c;
- key->dsa = pubkey->dsa_pub;
+#ifndef HAVE_LIBCRYPTO
key->rsa = pubkey->rsa_pub;
+#else
+ key->key = pubkey->key_pub;
+#endif /* HAVE_LIBCRYPTO */
rc = ssh_pki_export_pubkey_blob(key, &key_blob);
if (rc < 0) {
key_blob = NULL;
}
- key->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
key->rsa = NULL;
+#else
+ key->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(key);
return key_blob;
@@ -622,8 +638,12 @@ int ssh_publickey_to_file(ssh_session session,
fp = fopen(file, "w+");
if (fp == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Error opening %s: %s", file, strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(session,
+ SSH_REQUEST_DENIED,
+ "Error opening %s: %s",
+ file,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -735,7 +755,7 @@ int ssh_accept(ssh_session session) {
}
int channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
- return ssh_channel_write(channel, data, len);
+ return ssh_channel_write_stderr(channel, data, len);
}
/** @deprecated
diff --git a/src/libcrypto-compat.c b/src/libcrypto-compat.c
deleted file mode 100644
index 01ca70e7..00000000
--- a/src/libcrypto-compat.c
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
- *
- * Licensed under the OpenSSL license (the "License"). You may not use
- * this file except in compliance with the License. You can obtain a copy
- * in the file LICENSE in the source distribution or at
- * https://www.openssl.org/source/license.html
- */
-
-#include "config.h"
-
-#include <string.h>
-#include "libcrypto-compat.h"
-
-#ifndef OPENSSL_NO_ENGINE
-#include <openssl/engine.h>
-#endif
-
-static void *OPENSSL_zalloc(size_t num)
-{
- void *ret = OPENSSL_malloc(num);
-
- if (ret != NULL)
- memset(ret, 0, num);
- return ret;
-}
-
-int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
-{
- /* If the fields n and e in r are NULL, the corresponding input
- * parameters MUST be non-NULL for n and e. d may be
- * left NULL (in case only the public key is used).
- */
- if ((r->n == NULL && n == NULL)
- || (r->e == NULL && e == NULL))
- return 0;
-
- if (n != NULL) {
- BN_free(r->n);
- r->n = n;
- }
- if (e != NULL) {
- BN_free(r->e);
- r->e = e;
- }
- if (d != NULL) {
- BN_free(r->d);
- r->d = d;
- }
-
- return 1;
-}
-
-int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
-{
- /* If the fields p and q in r are NULL, the corresponding input
- * parameters MUST be non-NULL.
- */
- if ((r->p == NULL && p == NULL)
- || (r->q == NULL && q == NULL))
- return 0;
-
- if (p != NULL) {
- BN_free(r->p);
- r->p = p;
- }
- if (q != NULL) {
- BN_free(r->q);
- r->q = q;
- }
-
- return 1;
-}
-
-int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
-{
- /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input
- * parameters MUST be non-NULL.
- */
- if ((r->dmp1 == NULL && dmp1 == NULL)
- || (r->dmq1 == NULL && dmq1 == NULL)
- || (r->iqmp == NULL && iqmp == NULL))
- return 0;
-
- if (dmp1 != NULL) {
- BN_free(r->dmp1);
- r->dmp1 = dmp1;
- }
- if (dmq1 != NULL) {
- BN_free(r->dmq1);
- r->dmq1 = dmq1;
- }
- if (iqmp != NULL) {
- BN_free(r->iqmp);
- r->iqmp = iqmp;
- }
-
- return 1;
-}
-
-void RSA_get0_key(const RSA *r,
- const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
-{
- if (n != NULL)
- *n = r->n;
- if (e != NULL)
- *e = r->e;
- if (d != NULL)
- *d = r->d;
-}
-
-void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
-{
- if (p != NULL)
- *p = r->p;
- if (q != NULL)
- *q = r->q;
-}
-
-void RSA_get0_crt_params(const RSA *r,
- const BIGNUM **dmp1, const BIGNUM **dmq1,
- const BIGNUM **iqmp)
-{
- if (dmp1 != NULL)
- *dmp1 = r->dmp1;
- if (dmq1 != NULL)
- *dmq1 = r->dmq1;
- if (iqmp != NULL)
- *iqmp = r->iqmp;
-}
-
-void DSA_get0_pqg(const DSA *d,
- const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
-{
- if (p != NULL)
- *p = d->p;
- if (q != NULL)
- *q = d->q;
- if (g != NULL)
- *g = d->g;
-}
-
-int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
-{
- /* If the fields p, q and g in d are NULL, the corresponding input
- * parameters MUST be non-NULL.
- */
- if ((d->p == NULL && p == NULL)
- || (d->q == NULL && q == NULL)
- || (d->g == NULL && g == NULL))
- return 0;
-
- if (p != NULL) {
- BN_free(d->p);
- d->p = p;
- }
- if (q != NULL) {
- BN_free(d->q);
- d->q = q;
- }
- if (g != NULL) {
- BN_free(d->g);
- d->g = g;
- }
-
- return 1;
-}
-
-void DSA_get0_key(const DSA *d,
- const BIGNUM **pub_key, const BIGNUM **priv_key)
-{
- if (pub_key != NULL)
- *pub_key = d->pub_key;
- if (priv_key != NULL)
- *priv_key = d->priv_key;
-}
-
-int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
-{
- /* If the field pub_key in d is NULL, the corresponding input
- * parameters MUST be non-NULL. The priv_key field may
- * be left NULL.
- */
- if (d->pub_key == NULL && pub_key == NULL)
- return 0;
-
- if (pub_key != NULL) {
- BN_free(d->pub_key);
- d->pub_key = pub_key;
- }
- if (priv_key != NULL) {
- BN_free(d->priv_key);
- d->priv_key = priv_key;
- }
-
- return 1;
-}
-
-void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
-{
- if (pr != NULL)
- *pr = sig->r;
- if (ps != NULL)
- *ps = sig->s;
-}
-
-int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- if (r == NULL || s == NULL)
- return 0;
- BN_clear_free(sig->r);
- BN_clear_free(sig->s);
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
-{
- if (pr != NULL)
- *pr = sig->r;
- if (ps != NULL)
- *ps = sig->s;
-}
-
-int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- if (r == NULL || s == NULL)
- return 0;
- BN_clear_free(sig->r);
- BN_clear_free(sig->s);
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-EVP_MD_CTX *EVP_MD_CTX_new(void)
-{
- return OPENSSL_zalloc(sizeof(EVP_MD_CTX));
-}
-
-static void OPENSSL_clear_free(void *str, size_t num)
-{
- if (str == NULL)
- return;
- if (num)
- OPENSSL_cleanse(str, num);
- OPENSSL_free(str);
-}
-
-/* This call frees resources associated with the context */
-int EVP_MD_CTX_reset(EVP_MD_CTX *ctx)
-{
- if (ctx == NULL)
- return 1;
-
- /*
- * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because
- * sometimes only copies of the context are ever finalised.
- */
- if (ctx->digest && ctx->digest->cleanup
- && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED))
- ctx->digest->cleanup(ctx);
- if (ctx->digest && ctx->digest->ctx_size && ctx->md_data
- && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) {
- OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size);
- }
- EVP_PKEY_CTX_free(ctx->pctx);
-#ifndef OPENSSL_NO_ENGINE
- ENGINE_finish(ctx->engine);
-#endif
- OPENSSL_cleanse(ctx, sizeof(*ctx));
-
- return 1;
-}
-
-void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
-{
- EVP_MD_CTX_reset(ctx);
- OPENSSL_free(ctx);
-}
-
-int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx)
-{
- EVP_CIPHER_CTX_init(ctx);
- return 1;
-}
-
-HMAC_CTX *HMAC_CTX_new(void)
-{
- HMAC_CTX *ctx = OPENSSL_zalloc(sizeof(HMAC_CTX));
-
- if (ctx != NULL) {
- if (!HMAC_CTX_reset(ctx)) {
- HMAC_CTX_free(ctx);
- return NULL;
- }
- }
- return ctx;
-}
-
-static void hmac_ctx_cleanup(HMAC_CTX *ctx)
-{
- EVP_MD_CTX_reset(&ctx->i_ctx);
- EVP_MD_CTX_reset(&ctx->o_ctx);
- EVP_MD_CTX_reset(&ctx->md_ctx);
- ctx->md = NULL;
- ctx->key_length = 0;
- OPENSSL_cleanse(ctx->key, sizeof(ctx->key));
-}
-
-void HMAC_CTX_free(HMAC_CTX *ctx)
-{
- if (ctx != NULL) {
- hmac_ctx_cleanup(ctx);
-#if OPENSSL_VERSION_NUMBER > 0x10100000L
- EVP_MD_CTX_free(&ctx->i_ctx);
- EVP_MD_CTX_free(&ctx->o_ctx);
- EVP_MD_CTX_free(&ctx->md_ctx);
-#endif
- OPENSSL_free(ctx);
- }
-}
-
-int HMAC_CTX_reset(HMAC_CTX *ctx)
-{
- HMAC_CTX_init(ctx);
- return 1;
-}
-
-#ifndef HAVE_OPENSSL_EVP_CIPHER_CTX_NEW
-EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void)
-{
- return OPENSSL_zalloc(sizeof(EVP_CIPHER_CTX));
-}
-
-void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
-{
- /* EVP_CIPHER_CTX_reset(ctx); alias */
- EVP_CIPHER_CTX_init(ctx);
- OPENSSL_free(ctx);
-}
-#endif
-
-void DH_get0_pqg(const DH *dh,
- const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
-{
- if (p) {
- *p = dh->p;
- }
- if (q) {
- *q = NULL;
- }
- if (g) {
- *g = dh->g;
- }
-}
-
-int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
-{
- if (p) {
- if (dh->p) {
- BN_free(dh->p);
- }
- dh->p = p;
- }
- if (g) {
- if (dh->g) {
- BN_free(dh->g);
- }
- dh->g = g;
- }
- return 1;
-}
-
-void DH_get0_key(const DH *dh,
- const BIGNUM **pub_key, const BIGNUM **priv_key)
-{
- if (pub_key) {
- *pub_key = dh->pub_key;
- }
- if (priv_key) {
- *priv_key = dh->priv_key;
- }
-}
-
-int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
-{
- if (pub_key) {
- if (dh->pub_key) {
- BN_free(dh->pub_key);
- }
- dh->pub_key = pub_key;
- }
- if (priv_key) {
- if (dh->priv_key) {
- BN_free(dh->priv_key);
- }
- dh->priv_key = priv_key;
- }
- return 1;
-}
-
-const char *OpenSSL_version(int type)
-{
- return SSLeay_version(type);
-}
-unsigned long OpenSSL_version_num(void)
-{
- return SSLeay();
-}
diff --git a/src/libcrypto-compat.h b/src/libcrypto-compat.h
index 0082e207..0f2dc184 100644
--- a/src/libcrypto-compat.h
+++ b/src/libcrypto-compat.h
@@ -2,54 +2,13 @@
#define LIBCRYPTO_COMPAT_H
#include <openssl/opensslv.h>
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#include <openssl/ecdsa.h>
-#include <openssl/dh.h>
-#include <openssl/evp.h>
-#include <openssl/hmac.h>
-#include <openssl/bn.h>
-
-int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d);
-int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q);
-int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp);
-void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d);
-void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q);
-void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp);
-
-void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
-int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g);
-void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key);
-int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key);
-
-void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
-int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s);
-
-void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps);
-int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s);
-
-int EVP_MD_CTX_reset(EVP_MD_CTX *ctx);
-EVP_MD_CTX *EVP_MD_CTX_new(void);
-void EVP_MD_CTX_free(EVP_MD_CTX *ctx);
-
-int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx);
-
-HMAC_CTX *HMAC_CTX_new(void);
-int HMAC_CTX_reset(HMAC_CTX *ctx);
-void HMAC_CTX_free(HMAC_CTX *ctx);
-
-void DH_get0_pqg(const DH *dh,
- const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
-int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g);
-void DH_get0_key(const DH *dh,
- const BIGNUM **pub_key, const BIGNUM **priv_key);
-int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key);
-
-const char *OpenSSL_version(int type);
-unsigned long OpenSSL_version_num(void);
+#define NISTP256 "P-256"
+#define NISTP384 "P-384"
+#define NISTP521 "P-521"
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#define EVP_PKEY_eq EVP_PKEY_cmp
#endif /* OPENSSL_VERSION_NUMBER */
#endif /* LIBCRYPTO_COMPAT_H */
diff --git a/src/libcrypto.c b/src/libcrypto.c
index 96abec14..f45ffa96 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -26,26 +26,31 @@
#include <string.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
-#endif
+#endif /* HAVE_SYS_TIME_H */
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/wrapper.h"
#include "libssh/libcrypto.h"
-#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
+#include "libssh/pki.h"
+#ifdef HAVE_OPENSSL_EVP_CHACHA20
#include "libssh/bytearray.h"
#include "libssh/chacha20-poly1305-common.h"
#endif
#ifdef HAVE_LIBCRYPTO
+#include <openssl/opensslv.h>
#include <openssl/sha.h>
#include <openssl/md5.h>
-#include <openssl/dsa.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#include <openssl/rsa.h>
#include <openssl/hmac.h>
-#include <openssl/opensslv.h>
+#else
+#include <openssl/param_build.h>
+#include <openssl/core_names.h>
+#endif /* OPENSSL_VERSION_NUMBER */
#include <openssl/rand.h>
#include <openssl/engine.h>
@@ -54,11 +59,11 @@
#ifdef HAVE_OPENSSL_AES_H
#define HAS_AES
#include <openssl/aes.h>
-#endif
+#endif /* HAVE_OPENSSL_AES_H */
#ifdef HAVE_OPENSSL_DES_H
#define HAS_DES
#include <openssl/des.h>
-#endif
+#endif /* HAVE_OPENSSL_DES_H */
#if (defined(HAVE_VALGRIND_VALGRIND_H) && defined(HAVE_OPENSSL_IA32CAP_LOC))
#include <valgrind/valgrind.h>
@@ -67,18 +72,19 @@
#include "libssh/crypto.h"
-#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID
+#ifdef HAVE_OPENSSL_EVP_KDF_CTX
#include <openssl/kdf.h>
-#endif
-
-#ifdef HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT
-#include <openssl/modes.h>
-#endif
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/param_build.h>
+#include <openssl/core_names.h>
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* HAVE_OPENSSL_EVP_KDF_CTX */
#include "libssh/crypto.h"
static int libcrypto_initialized = 0;
+
void ssh_reseed(void){
#ifndef _WIN32
struct timeval tv;
@@ -87,270 +93,42 @@ void ssh_reseed(void){
#endif
}
-/**
- * @brief Get random bytes
- *
- * Make sure to always check the return code of this function!
- *
- * @param[in] where The buffer to fill with random bytes
- *
- * @param[in] len The size of the buffer to fill.
- *
- * @param[in] strong Use a strong or private RNG source.
- *
- * @return 1 on success, 0 on error.
- */
-int ssh_get_random(void *where, int len, int strong)
-{
-#ifdef HAVE_OPENSSL_RAND_PRIV_BYTES
- if (strong) {
- /* Returns -1 when not supported, 0 on error, 1 on success */
- return !!RAND_priv_bytes(where, len);
- }
-#else
- (void)strong;
-#endif /* HAVE_RAND_PRIV_BYTES */
-
- /* Returns -1 when not supported, 0 on error, 1 on success */
- return !!RAND_bytes(where, len);
-}
+#ifndef WITH_PKCS11_PROVIDER
+static ENGINE *engine = NULL;
-SHACTX sha1_init(void)
+ENGINE *pki_get_engine(void)
{
- int rc;
- SHACTX c = EVP_MD_CTX_create();
- if (c == NULL) {
- return NULL;
- }
- EVP_MD_CTX_init(c);
- rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_destroy(c);
- c = NULL;
- }
- return c;
-}
-
-void sha1_update(SHACTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
+ int ok;
-void sha1_final(unsigned char *md, SHACTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
-}
-
-void sha1(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHACTX c = sha1_init();
- if (c != NULL) {
- sha1_update(c, digest, len);
- sha1_final(hash, c);
- }
-}
+ if (engine == NULL) {
+ ENGINE_load_builtin_engines();
-#ifdef HAVE_OPENSSL_ECC
-static const EVP_MD *nid_to_evpmd(int nid)
-{
- switch (nid) {
- case NID_X9_62_prime256v1:
- return EVP_sha256();
- case NID_secp384r1:
- return EVP_sha384();
- case NID_secp521r1:
- return EVP_sha512();
- default:
+ engine = ENGINE_by_id("pkcs11");
+ if (engine == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Could not load the engine: %s",
+ ERR_error_string(ERR_get_error(), NULL));
return NULL;
- }
-
- return NULL;
-}
-
-void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen)
-{
- const EVP_MD *evp_md = nid_to_evpmd(nid);
- EVP_MD_CTX *md = EVP_MD_CTX_new();
-
- EVP_DigestInit(md, evp_md);
- EVP_DigestUpdate(md, digest, len);
- EVP_DigestFinal(md, hash, hlen);
- EVP_MD_CTX_free(md);
-}
-
-EVPCTX evp_init(int nid)
-{
- const EVP_MD *evp_md = nid_to_evpmd(nid);
-
- EVPCTX ctx = EVP_MD_CTX_new();
- if (ctx == NULL) {
- return NULL;
- }
-
- EVP_DigestInit(ctx, evp_md);
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- EVP_DigestFinal(ctx, md, mdlen);
- EVP_MD_CTX_free(ctx);
-}
-#endif
-
-SHA256CTX sha256_init(void)
-{
- int rc;
- SHA256CTX c = EVP_MD_CTX_create();
- if (c == NULL) {
- return NULL;
- }
- EVP_MD_CTX_init(c);
- rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_destroy(c);
- c = NULL;
- }
- return c;
-}
-
-void sha256_update(SHA256CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha256_final(unsigned char *md, SHA256CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
-}
-
-void sha256(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHA256CTX c = sha256_init();
- if (c != NULL) {
- sha256_update(c, digest, len);
- sha256_final(hash, c);
- }
-}
-
-SHA384CTX sha384_init(void)
-{
- int rc;
- SHA384CTX c = EVP_MD_CTX_create();
- if (c == NULL) {
- return NULL;
- }
- EVP_MD_CTX_init(c);
- rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_destroy(c);
- c = NULL;
- }
- return c;
-}
-
-void sha384_update(SHA384CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha384_final(unsigned char *md, SHA384CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
-}
-
-void sha384(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHA384CTX c = sha384_init();
- if (c != NULL) {
- sha384_update(c, digest, len);
- sha384_final(hash, c);
- }
-}
-
-SHA512CTX sha512_init(void)
-{
- int rc = 0;
- SHA512CTX c = EVP_MD_CTX_create();
- if (c == NULL) {
- return NULL;
- }
- EVP_MD_CTX_init(c);
- rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL);
- if (rc == 0) {
- EVP_MD_CTX_destroy(c);
- c = NULL;
- }
- return c;
-}
-
-void sha512_update(SHA512CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void sha512_final(unsigned char *md, SHA512CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
-}
-
-void sha512(const unsigned char *digest, int len, unsigned char *hash)
-{
- SHA512CTX c = sha512_init();
- if (c != NULL) {
- sha512_update(c, digest, len);
- sha512_final(hash, c);
- }
-}
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Engine loaded successfully");
+
+ ok = ENGINE_init(engine);
+ if (!ok) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Could not initialize the engine: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ ENGINE_free(engine);
+ return NULL;
+ }
-MD5CTX md5_init(void)
-{
- int rc;
- MD5CTX c = EVP_MD_CTX_create();
- if (c == NULL) {
- return NULL;
- }
- EVP_MD_CTX_init(c);
- rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
- if(rc == 0) {
- EVP_MD_CTX_destroy(c);
- c = NULL;
+ SSH_LOG(SSH_LOG_DEBUG, "Engine init success");
}
- return c;
-}
-
-void md5_update(MD5CTX c, const void *data, unsigned long len)
-{
- EVP_DigestUpdate(c, data, len);
-}
-
-void md5_final(unsigned char *md, MD5CTX c)
-{
- unsigned int mdlen = 0;
-
- EVP_DigestFinal(c, md, &mdlen);
- EVP_MD_CTX_destroy(c);
+ return engine;
}
+#endif /* WITH_PKCS11_PROVIDER */
-#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID
+#ifdef HAVE_OPENSSL_EVP_KDF_CTX
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
{
switch (digest_type) {
@@ -365,19 +143,50 @@ static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
}
return NULL;
}
+#else
+static const char *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type)
+{
+ switch (digest_type) {
+ case SSH_KDF_SHA1:
+ return SN_sha1;
+ case SSH_KDF_SHA256:
+ return SN_sha256;
+ case SSH_KDF_SHA384:
+ return SN_sha384;
+ case SSH_KDF_SHA512:
+ return SN_sha512;
+ }
+ return NULL;
+}
+#endif /* OPENSSL_VERSION_NUMBER */
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
+ int rc = -1;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EVP_KDF_CTX *ctx = EVP_KDF_CTX_new_id(EVP_KDF_SSHKDF);
- int rc;
+#else
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSHKDF", NULL);
+ EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ OSSL_PARAM *params = NULL;
+ const char *md = sshkdf_digest_to_md(crypto->digest_type);
+
+ EVP_KDF_free(kdf);
+ if (param_bld == NULL) {
+ EVP_KDF_CTX_free(ctx);
+ return -1;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
if (ctx == NULL) {
- return -1;
+ goto out;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD,
sshkdf_digest_to_md(crypto->digest_type));
if (rc != 1) {
@@ -397,7 +206,7 @@ int ssh_kdf(struct ssh_crypto_struct *crypto,
goto out;
}
rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID,
- crypto->session_id, crypto->digest_len);
+ crypto->session_id, crypto->session_id_len);
if (rc != 1) {
goto out;
}
@@ -405,8 +214,60 @@ int ssh_kdf(struct ssh_crypto_struct *crypto,
if (rc != 1) {
goto out;
}
+#else
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_KDF_PARAM_DIGEST,
+ md, strlen(md));
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_KDF_PARAM_KEY,
+ key, key_len);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld,
+ OSSL_KDF_PARAM_SSHKDF_XCGHASH,
+ crypto->secret_hash,
+ crypto->digest_len);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld,
+ OSSL_KDF_PARAM_SSHKDF_SESSION_ID,
+ crypto->session_id,
+ crypto->session_id_len);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_KDF_PARAM_SSHKDF_TYPE,
+ (const char*)&key_type, 1);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ rc = -1;
+ goto out;
+ }
+
+ rc = EVP_KDF_derive(ctx, output, requested_len, params);
+ if (rc != 1) {
+ rc = -1;
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
out:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD_free(param_bld);
+ OSSL_PARAM_free(params);
+#endif
EVP_KDF_CTX_free(ctx);
if (rc < 0) {
return rc;
@@ -417,64 +278,83 @@ out:
#else
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
return sshkdf_derive_key(crypto, key, key_len,
key_type, output, requested_len);
}
-#endif
+#endif /* HAVE_OPENSSL_EVP_KDF_CTX */
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
- HMACCTX ctx = NULL;
+HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type)
+{
+ HMACCTX ctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ int rc = -1;
- ctx = HMAC_CTX_new();
- if (ctx == NULL) {
- return NULL;
- }
+ ctx = EVP_MD_CTX_new();
+ if (ctx == NULL) {
+ return NULL;
+ }
+ pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, len);
+ if (pkey == NULL) {
+ goto error;
+ }
- switch(type) {
+ switch (type) {
case SSH_HMAC_SHA1:
- HMAC_Init_ex(ctx, key, len, EVP_sha1(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_sha1(), NULL, pkey);
+ break;
case SSH_HMAC_SHA256:
- HMAC_Init_ex(ctx, key, len, EVP_sha256(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
+ break;
case SSH_HMAC_SHA512:
- HMAC_Init_ex(ctx, key, len, EVP_sha512(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_sha512(), NULL, pkey);
+ break;
case SSH_HMAC_MD5:
- HMAC_Init_ex(ctx, key, len, EVP_md5(), NULL);
- break;
+ rc = EVP_DigestSignInit(ctx, NULL, EVP_md5(), NULL, pkey);
+ break;
default:
- HMAC_CTX_free(ctx);
- ctx = NULL;
- }
+ rc = -1;
+ break;
+ }
+
+ EVP_PKEY_free(pkey);
+ if (rc != 1) {
+ goto error;
+ }
+ return ctx;
- return ctx;
+error:
+ EVP_MD_CTX_free(ctx);
+ return NULL;
}
-void hmac_update(HMACCTX ctx, const void *data, unsigned long len) {
- HMAC_Update(ctx, data, len);
+int hmac_update(HMACCTX ctx, const void *data, size_t len)
+{
+ return EVP_DigestSignUpdate(ctx, data, len);
}
-void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) {
- HMAC_Final(ctx,hashmacbuf,len);
+int hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, size_t *len)
+{
+ size_t res = *len;
+ int rc;
+ rc = EVP_DigestSignFinal(ctx, hashmacbuf, &res);
+ EVP_MD_CTX_free(ctx);
+ if (rc == 1) {
+ *len = res;
+ }
-#if OPENSSL_VERSION_NUMBER > 0x10100000L
- HMAC_CTX_free(ctx);
- ctx = NULL;
-#else
- HMAC_cleanup(ctx);
- SAFE_FREE(ctx);
- ctx = NULL;
-#endif
+ return rc;
}
-static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
+static void evp_cipher_init(struct ssh_cipher_struct *cipher)
+{
if (cipher->ctx == NULL) {
cipher->ctx = EVP_CIPHER_CTX_new();
+ } else {
+ EVP_CIPHER_CTX_init(cipher->ctx);
}
switch(cipher->ciphertype){
@@ -487,7 +367,6 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
case SSH_AES256_CBC:
cipher->cipher = EVP_aes_256_cbc();
break;
-#ifdef HAVE_OPENSSL_EVP_AES_CTR
case SSH_AES128_CTR:
cipher->cipher = EVP_aes_128_ctr();
break;
@@ -497,26 +376,12 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
case SSH_AES256_CTR:
cipher->cipher = EVP_aes_256_ctr();
break;
-#else
- case SSH_AES128_CTR:
- case SSH_AES192_CTR:
- case SSH_AES256_CTR:
- SSH_LOG(SSH_LOG_WARNING, "This cipher is not available in evp_cipher_init");
- break;
-#endif
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
case SSH_AEAD_AES128_GCM:
cipher->cipher = EVP_aes_128_gcm();
break;
case SSH_AEAD_AES256_GCM:
cipher->cipher = EVP_aes_256_gcm();
break;
-#else
- case SSH_AEAD_AES128_GCM:
- case SSH_AEAD_AES256_GCM:
- SSH_LOG(SSH_LOG_WARNING, "This cipher is not available in evp_cipher_init");
- break;
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
case SSH_3DES_CBC:
cipher->cipher = EVP_des_ede3_cbc();
break;
@@ -525,12 +390,12 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher) {
cipher->cipher = EVP_bf_cbc();
break;
/* ciphers not using EVP */
-#endif
+#endif /* WITH_BLOWFISH_CIPHER */
case SSH_AEAD_CHACHA20_POLY1305:
- SSH_LOG(SSH_LOG_WARNING, "The ChaCha cipher cannot be handled here");
+ SSH_LOG(SSH_LOG_TRACE, "The ChaCha cipher cannot be handled here");
break;
case SSH_NO_CIPHER:
- SSH_LOG(SSH_LOG_WARNING, "No valid ciphertype found");
+ SSH_LOG(SSH_LOG_TRACE, "No valid ciphertype found");
break;
}
}
@@ -541,15 +406,13 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
int rc;
evp_cipher_init(cipher);
- EVP_CIPHER_CTX_reset(cipher->ctx);
rc = EVP_EncryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptInit_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptInit_ex failed");
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
/* For AES-GCM we need to set IV in specific way */
if (cipher->ciphertype == SSH_AEAD_AES128_GCM ||
cipher->ciphertype == SSH_AEAD_AES256_GCM) {
@@ -558,11 +421,10 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
-1,
(uint8_t *)IV);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_IV_FIXED failed");
return SSH_ERROR;
}
}
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
EVP_CIPHER_CTX_set_padding(cipher->ctx, 0);
@@ -574,15 +436,13 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
int rc;
evp_cipher_init(cipher);
- EVP_CIPHER_CTX_reset(cipher->ctx);
rc = EVP_DecryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptInit_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptInit_ex failed");
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
/* For AES-GCM we need to set IV in specific way */
if (cipher->ciphertype == SSH_AEAD_AES128_GCM ||
cipher->ciphertype == SSH_AEAD_AES256_GCM) {
@@ -591,11 +451,10 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
-1,
(uint8_t *)IV);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_IV_FIXED failed");
return SSH_ERROR;
}
}
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
EVP_CIPHER_CTX_set_padding(cipher->ctx, 0);
@@ -617,11 +476,11 @@ static void evp_cipher_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)in,
(int)len);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
return;
}
if (outlen != (int)len){
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG,
"EVP_EncryptUpdate: output size %d for %zu in",
outlen,
len);
@@ -643,11 +502,11 @@ static void evp_cipher_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)in,
(int)len);
if (rc != 1){
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed");
return;
}
if (outlen != (int)len){
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_DEBUG,
"EVP_DecryptUpdate: output size %d for %zu in",
outlen,
len);
@@ -661,68 +520,6 @@ static void evp_cipher_cleanup(struct ssh_cipher_struct *cipher) {
}
}
-#ifndef HAVE_OPENSSL_EVP_AES_CTR
-/* Some OS (osx, OpenIndiana, ...) have no support for CTR ciphers in EVP_aes */
-
-struct ssh_aes_key_schedule {
- AES_KEY key;
- uint8_t IV[AES_BLOCK_SIZE];
-};
-
-static int aes_ctr_set_key(struct ssh_cipher_struct *cipher, void *key,
- void *IV) {
- int rc;
-
- if (cipher->aes_key == NULL) {
- cipher->aes_key = malloc(sizeof (struct ssh_aes_key_schedule));
- }
- if (cipher->aes_key == NULL) {
- return SSH_ERROR;
- }
- ZERO_STRUCTP(cipher->aes_key);
- /* CTR doesn't need a decryption key */
- rc = AES_set_encrypt_key(key, cipher->keysize, &cipher->aes_key->key);
- if (rc < 0) {
- SAFE_FREE(cipher->aes_key);
- return SSH_ERROR;
- }
- memcpy(cipher->aes_key->IV, IV, AES_BLOCK_SIZE);
- return SSH_OK;
-}
-
-static void
-aes_ctr_encrypt(struct ssh_cipher_struct *cipher,
- void *in,
- void *out,
- size_t len)
-{
- unsigned char tmp_buffer[AES_BLOCK_SIZE];
- unsigned int num=0;
- /* Some things are special with ctr128 :
- * In this case, tmp_buffer is not being used, because it is used to store temporary data
- * when an encryption is made on lengths that are not multiple of blocksize.
- * Same for num, which is being used to store the current offset in blocksize in CTR
- * function.
- */
-#ifdef HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT
- CRYPTO_ctr128_encrypt(in, out, len, &cipher->aes_key->key, cipher->aes_key->IV, tmp_buffer, &num, (block128_f)AES_encrypt);
-#else
- AES_ctr128_encrypt(in, out, len, &cipher->aes_key->key, cipher->aes_key->IV, tmp_buffer, &num);
-#endif /* HAVE_OPENSSL_CRYPTO_CTR128_ENCRYPT */
-}
-
-static void aes_ctr_cleanup(struct ssh_cipher_struct *cipher){
- if (cipher != NULL) {
- if (cipher->aes_key != NULL) {
- explicit_bzero(cipher->aes_key, sizeof(*cipher->aes_key));
- }
- SAFE_FREE(cipher->aes_key);
- }
-}
-
-#endif /* HAVE_OPENSSL_EVP_AES_CTR */
-
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
static int
evp_cipher_aead_get_length(struct ssh_cipher_struct *cipher,
void *in,
@@ -764,7 +561,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
1,
lastiv);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed");
return;
}
@@ -776,7 +573,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
(int)aadlen);
outlen = tmplen;
if (rc == 0 || outlen != aadlen) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to pass authenticated data");
return;
}
memcpy(out, in, aadlen);
@@ -789,7 +586,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
(int)len - aadlen);
outlen = tmplen;
if (rc != 1 || outlen != (int)len - aadlen) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
return;
}
@@ -798,7 +595,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
NULL,
&tmplen);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptFinal failed: Failed to create a tag");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal failed: Failed to create a tag");
return;
}
@@ -807,7 +604,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
authlen,
(unsigned char *)tag);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_GET_TAG failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_GET_TAG failed");
return;
}
}
@@ -835,7 +632,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
1,
lastiv);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed");
return SSH_ERROR;
}
@@ -845,7 +642,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
authlen,
(unsigned char *)complete_packet + aadlen + encrypted_size);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_TAG failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_TAG failed");
return SSH_ERROR;
}
@@ -856,7 +653,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)complete_packet,
(int)aadlen);
if (rc == 0) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to pass authenticated data");
return SSH_ERROR;
}
/* Do not copy the length to the target buffer, because it is already processed */
@@ -867,14 +664,14 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)out,
&outlen,
(unsigned char *)complete_packet + aadlen,
- encrypted_size /* already substracted aadlen*/);
+ encrypted_size /* already subtracted aadlen */);
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed");
return SSH_ERROR;
}
if (outlen != (int)encrypted_size) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"EVP_DecryptUpdate: output size %d for %zd in",
outlen,
encrypted_size);
@@ -886,28 +683,31 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
NULL,
&outlen);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptFinal failed: Failed authentication");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptFinal failed: Failed authentication");
return SSH_ERROR;
}
return SSH_OK;
}
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
-
-#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
+#ifdef HAVE_OPENSSL_EVP_CHACHA20
struct chacha20_poly1305_keysched {
/* cipher handle used for encrypting the packets */
EVP_CIPHER_CTX *main_evp;
/* cipher handle used for encrypting the length field */
EVP_CIPHER_CTX *header_evp;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* mac handle used for authenticating the packets */
EVP_PKEY_CTX *pctx;
/* Poly1305 key */
EVP_PKEY *key;
/* MD context for digesting data in poly1305 */
EVP_MD_CTX *mctx;
+#else
+ /* MAC context used to do poly1305 */
+ EVP_MAC_CTX *mctx;
+#endif /* OPENSSL_VERSION_NUMBER */
};
static void
@@ -925,11 +725,16 @@ chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher)
ctx->main_evp = NULL;
EVP_CIPHER_CTX_free(ctx->header_evp);
ctx->header_evp = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* ctx->pctx is freed as part of MD context */
EVP_PKEY_free(ctx->key);
ctx->key = NULL;
EVP_MD_CTX_free(ctx->mctx);
ctx->mctx = NULL;
+#else
+ EVP_MAC_CTX_free(ctx->mctx);
+ ctx->mctx = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
SAFE_FREE(cipher->chacha20_schedule);
}
@@ -942,6 +747,9 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
struct chacha20_poly1305_keysched *ctx = NULL;
uint8_t *u8key = key;
int ret = SSH_ERROR, rv;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *mac = NULL;
+#endif
if (cipher->chacha20_schedule == NULL) {
ctx = calloc(1, sizeof(*ctx));
@@ -957,38 +765,54 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
/* K2 uses the first half of the key */
ctx->main_evp = EVP_CIPHER_CTX_new();
if (ctx->main_evp == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CIPHER_CTX_new failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CIPHER_CTX_new failed");
goto out;
}
rv = EVP_EncryptInit_ex(ctx->main_evp, EVP_chacha20(), NULL, u8key, NULL);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit failed");
goto out;
}
/* K1 uses the second half of the key */
ctx->header_evp = EVP_CIPHER_CTX_new();
if (ctx->header_evp == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CIPHER_CTX_new failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CIPHER_CTX_new failed");
goto out;
}
ret = EVP_EncryptInit_ex(ctx->header_evp, EVP_chacha20(), NULL,
u8key + CHACHA20_KEYLEN, NULL);
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit failed");
goto out;
}
/* The Poly1305 key initialization is delayed to the time we know
* the actual key for packet so we do not need to create a bogus keys
*/
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
ctx->mctx = EVP_MD_CTX_new();
if (ctx->mctx == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_MD_CTX_new failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MD_CTX_new failed");
return SSH_ERROR;
}
+#else
+ mac = EVP_MAC_fetch(NULL, "poly1305", NULL);
+ if (mac == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_fetch failed");
+ goto out;
+ }
+ ctx->mctx = EVP_MAC_CTX_new(mac);
+ if (ctx->mctx == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_CTX_new failed");
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
ret = SSH_OK;
out:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_free(mac);
+#endif
if (ret != SSH_OK) {
chacha20_poly1305_cleanup(cipher);
}
@@ -1017,13 +841,13 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
ret = EVP_CipherInit_ex(ctx->header_evp, NULL, NULL, NULL, seqbuf, do_encrypt);
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit_ex(header_evp) failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit_ex(header_evp) failed");
return SSH_ERROR;
}
ret = EVP_CipherInit_ex(ctx->main_evp, NULL, NULL, NULL, seqbuf, do_encrypt);
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit_ex(main_evp) failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit_ex(main_evp) failed");
return SSH_ERROR;
}
@@ -1052,7 +876,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
rv = EVP_CipherUpdate(ctx->main_evp, poly_key, &len,
(unsigned char *)zero_block, sizeof(zero_block));
if (rv != 1 || len != CHACHA20_BLOCKSIZE) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed");
goto out;
}
#ifdef DEBUG_CRYPTO
@@ -1060,17 +884,18 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Set the Poly1305 key */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (ctx->key == NULL) {
/* Poly1305 Initialization needs to know the actual key */
ctx->key = EVP_PKEY_new_mac_key(EVP_PKEY_POLY1305, NULL,
poly_key, POLY1305_KEYLEN);
if (ctx->key == NULL) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_PKEY_new_mac_key failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_new_mac_key failed");
goto out;
}
rv = EVP_DigestSignInit(ctx->mctx, &ctx->pctx, NULL, NULL, ctx->key);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignInit failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignInit failed");
goto out;
}
} else {
@@ -1079,10 +904,17 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
EVP_PKEY_CTRL_SET_MAC_KEY,
POLY1305_KEYLEN, (void *)poly_key);
if (rv <= 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_PKEY_CTX_ctrl failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_CTX_ctrl failed");
goto out;
}
}
+#else
+ rv = EVP_MAC_init(ctx->mctx, poly_key, POLY1305_KEYLEN, NULL);
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_init failed");
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
ret = SSH_OK;
out:
@@ -1116,7 +948,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
rv = EVP_CipherUpdate(ctx->header_evp, out, &outlen, in, len);
if (rv != 1 || outlen != sizeof(uint32_t)) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
return SSH_ERROR;
}
@@ -1126,7 +958,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
rv = EVP_CipherFinal_ex(ctx->header_evp, out + outlen, &outlen);
if (rv != 1 || outlen != 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherFinal_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherFinal_ex failed");
return SSH_ERROR;
}
@@ -1151,7 +983,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
rv = chacha20_poly1305_packet_setup(cipher, seq, 0);
if (rv != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
goto out;
}
@@ -1160,25 +992,40 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Calculate MAC of received data */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
rv = EVP_DigestSignUpdate(ctx->mctx, complete_packet,
encrypted_size + sizeof(uint32_t));
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed");
goto out;
}
rv = EVP_DigestSignFinal(ctx->mctx, tag, &taglen);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "poly1305 verify error");
+ SSH_LOG(SSH_LOG_TRACE, "poly1305 verify error");
+ goto out;
+ }
+#else
+ rv = EVP_MAC_update(ctx->mctx, complete_packet,
+ encrypted_size + sizeof(uint32_t));
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed");
goto out;
}
+ rv = EVP_MAC_final(ctx->mctx, tag, &taglen, POLY1305_TAGLEN);
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_final failed");
+ goto out;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("calculated mac", tag, POLY1305_TAGLEN);
#endif /* DEBUG_CRYPTO */
/* Verify the calculated MAC matches the attached MAC */
- cmp = memcmp(tag, mac, POLY1305_TAGLEN);
+ cmp = CRYPTO_memcmp(tag, mac, POLY1305_TAGLEN);
if (cmp != 0) {
/* mac error */
SSH_LOG(SSH_LOG_PACKET, "poly1305 verify error");
@@ -1190,13 +1037,13 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)complete_packet + sizeof(uint32_t),
encrypted_size);
if (rv != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
goto out;
}
rv = EVP_CipherFinal_ex(ctx->main_evp, out + len, &len);
if (rv != 1 || len != 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherFinal_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherFinal_ex failed");
goto out;
}
@@ -1221,7 +1068,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
ret = chacha20_poly1305_packet_setup(cipher, seq, 1);
if (ret != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
return;
}
@@ -1236,7 +1083,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)&in_packet->length,
sizeof(uint32_t));
if (ret != 1 || outlen != sizeof(uint32_t)) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
return;
}
#ifdef DEBUG_CRYPTO
@@ -1245,7 +1092,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
ret = EVP_CipherFinal_ex(ctx->header_evp, (uint8_t *)out + outlen, &outlen);
if (ret != 1 || outlen != 0) {
- SSH_LOG(SSH_LOG_PACKET, "EVP_EncryptFinal_ex failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal_ex failed");
return;
}
@@ -1257,23 +1104,37 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
in_packet->payload,
len - sizeof(uint32_t));
if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed");
return;
}
/* step 4, compute the MAC */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
ret = EVP_DigestSignUpdate(ctx->mctx, out_packet, len);
if (ret <= 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignUpdate failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed");
return;
}
ret = EVP_DigestSignFinal(ctx->mctx, tag, &taglen);
if (ret <= 0) {
- SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignFinal failed");
+ SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignFinal failed");
return;
}
+#else
+ ret = EVP_MAC_update(ctx->mctx, (void*)out_packet, len);
+ if (ret != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed");
+ return;
+ }
+
+ ret = EVP_MAC_final(ctx->mctx, tag, &taglen, POLY1305_TAGLEN);
+ if (ret != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_final failed");
+ return;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
}
-#endif /* defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) */
+#endif /* HAVE_OPENSSL_EVP_CHACHA20 */
#ifdef WITH_INSECURE_NONE
static void
@@ -1302,13 +1163,8 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
-#endif
+#endif /* WITH_BLOWFISH_CIPHER */
#ifdef HAS_AES
-#ifndef BROKEN_AES_CTR
-/* OpenSSL until 0.9.7c has a broken AES_ctr128_encrypt implementation which
- * increments the counter from 2^64 instead of 1. It's better not to use it
- */
-#ifdef HAVE_OPENSSL_EVP_AES_CTR
{
.name = "aes128-ctr",
.blocksize = AES_BLOCK_SIZE,
@@ -1342,42 +1198,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
-#else /* HAVE_OPENSSL_EVP_AES_CTR */
- {
- .name = "aes128-ctr",
- .blocksize = AES_BLOCK_SIZE,
- .ciphertype = SSH_AES128_CTR,
- .keysize = 128,
- .set_encrypt_key = aes_ctr_set_key,
- .set_decrypt_key = aes_ctr_set_key,
- .encrypt = aes_ctr_encrypt,
- .decrypt = aes_ctr_encrypt,
- .cleanup = aes_ctr_cleanup
- },
- {
- .name = "aes192-ctr",
- .blocksize = AES_BLOCK_SIZE,
- .ciphertype = SSH_AES192_CTR,
- .keysize = 192,
- .set_encrypt_key = aes_ctr_set_key,
- .set_decrypt_key = aes_ctr_set_key,
- .encrypt = aes_ctr_encrypt,
- .decrypt = aes_ctr_encrypt,
- .cleanup = aes_ctr_cleanup
- },
- {
- .name = "aes256-ctr",
- .blocksize = AES_BLOCK_SIZE,
- .ciphertype = SSH_AES256_CTR,
- .keysize = 256,
- .set_encrypt_key = aes_ctr_set_key,
- .set_decrypt_key = aes_ctr_set_key,
- .encrypt = aes_ctr_encrypt,
- .decrypt = aes_ctr_encrypt,
- .cleanup = aes_ctr_cleanup
- },
-#endif /* HAVE_OPENSSL_EVP_AES_CTR */
-#endif /* BROKEN_AES_CTR */
{
.name = "aes128-cbc",
.blocksize = AES_BLOCK_SIZE,
@@ -1411,7 +1231,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup
},
-#ifdef HAVE_OPENSSL_EVP_AES_GCM
{
.name = "aes128-gcm@openssh.com",
.blocksize = AES_BLOCK_SIZE,
@@ -1440,7 +1259,6 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.aead_decrypt = evp_cipher_aead_decrypt,
.cleanup = evp_cipher_cleanup
},
-#endif /* HAVE_OPENSSL_EVP_AES_GCM */
#endif /* HAS_AES */
#ifdef HAS_DES
{
@@ -1456,7 +1274,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
},
#endif /* HAS_DES */
{
-#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305)
+#ifdef HAVE_OPENSSL_EVP_CHACHA20
.ciphertype = SSH_AEAD_CHACHA20_POLY1305,
.name = "chacha20-poly1305@openssh.com",
.blocksize = CHACHA20_BLOCKSIZE/8,
@@ -1472,7 +1290,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.cleanup = chacha20_poly1305_cleanup
#else
.name = "chacha20-poly1305@openssh.com"
-#endif /* defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) */
+#endif /* HAVE_OPENSSL_EVP_CHACHA20 */
},
#ifdef WITH_INSECURE_NONE
{
@@ -1499,13 +1317,15 @@ struct ssh_cipher_struct *ssh_get_ciphertab(void)
*/
int ssh_crypto_init(void)
{
- UNUSED_VAR(size_t i);
+#ifndef HAVE_OPENSSL_EVP_CHACHA20
+ size_t i;
+#endif
if (libcrypto_initialized) {
return SSH_OK;
}
if (OpenSSL_version_num() != OPENSSL_VERSION_NUMBER){
- SSH_LOG(SSH_LOG_WARNING, "libssh compiled with %s "
+ SSH_LOG(SSH_LOG_DEBUG, "libssh compiled with %s "
"headers, currently running with %s.",
OPENSSL_VERSION_TEXT,
OpenSSL_version(OpenSSL_version_num())
@@ -1521,12 +1341,9 @@ int ssh_crypto_init(void)
/* Bit #57 denotes AES-NI instruction set extension */
OPENSSL_ia32cap &= ~(1LL << 57);
}
-#endif
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- OpenSSL_add_all_algorithms();
-#endif
+#endif /* CAN_DISABLE_AESNI */
-#if !defined(HAVE_OPENSSL_EVP_CHACHA20) || !defined(HAVE_OPENSSL_EVP_POLY1305)
+#ifndef HAVE_OPENSSL_EVP_CHACHA20
for (i = 0; ssh_ciphertab[i].name != NULL; i++) {
int cmp;
@@ -1538,7 +1355,7 @@ int ssh_crypto_init(void)
break;
}
}
-#endif /* !defined(HAVE_OPENSSL_EVP_CHACHA20) || !defined(HAVE_OPENSSL_EVP_POLY1305) */
+#endif /* HAVE_OPENSSL_EVP_CHACHA20 */
libcrypto_initialized = 1;
@@ -1555,13 +1372,210 @@ void ssh_crypto_finalize(void)
return;
}
- ENGINE_cleanup();
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- EVP_cleanup();
- CRYPTO_cleanup_all_ex_data();
+/* TODO this should finalize engine if it was started, but during atexit calls,
+ * we are crashing. AFAIK this is related to the dlopened pkcs11 modules calling
+ * the crypto cleanups earlier. */
+#if 0
+ if (engine != NULL) {
+ ENGINE_finish(engine);
+ ENGINE_free(engine);
+ engine = NULL;
+ }
#endif
libcrypto_initialized = 0;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+/**
+ * @internal
+ * @brief Create EVP_PKEY from parameters
+ *
+ * @param[in] name Algorithm to use. For more info see manpage of EVP_PKEY_CTX_new_from_name
+ *
+ * @param[in] param_bld Constructed param builder for the pkey
+ *
+ * @param[out] pkey Created EVP_PKEY variable
+ *
+ * @param[in] selection Reference selections at man EVP_PKEY_FROMDATA
+ *
+ * @return 0 on success, -1 on error
+ */
+int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld,
+ EVP_PKEY **pkey, int selection)
+{
+ int rc;
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL);
+ OSSL_PARAM *params = NULL;
+
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ params = OSSL_PARAM_BLD_to_param(param_bld);
+ if (params == NULL) {
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_fromdata_init(ctx);
+ if (rc != 1) {
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ rc = EVP_PKEY_fromdata(ctx, pkey, selection, params);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Failed to import private key: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+
+ return SSH_OK;
+}
+
+/**
+ * @brief creates a copy of EVP_PKEY
+ *
+ * @param[in] name Algorithm to use. For more info see manpage of
+ * EVP_PKEY_CTX_new_from_name
+ *
+ * @param[in] key Key being duplicated from
+ *
+ * @param[in] demote Same as at pki_key_dup, only the public
+ * part of the key gets duplicated if true
+ *
+ * @param[out] new_key The key where the duplicate is saved
+ *
+ * @return 0 on success, -1 on error
+ */
+static int
+evp_dup_pkey(const char *name, const ssh_key key, int demote, ssh_key new_key)
+{
+ int rc;
+ EVP_PKEY_CTX *ctx = NULL;
+ OSSL_PARAM *params = NULL;
+
+ /* The simple case -- just reference the existing key */
+ if (!demote || (key->flags & SSH_KEY_FLAG_PRIVATE) == 0) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ return -1;
+ }
+ new_key->key = key->key;
+ return SSH_OK;
+ }
+
+ /* demote == 1 */
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL);
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, &params);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ return -1;
+ }
+
+ if (strcmp(name, "EC") == 0) {
+ OSSL_PARAM *locate_param = NULL;
+ /* For ECC keys provided by engine or provider, we need to have the
+ * explicit public part available, otherwise the key will not be
+ * usable */
+ locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (locate_param == NULL) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return -1;
+ }
+ }
+ rc = EVP_PKEY_fromdata_init(ctx);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return -1;
+ }
+
+ rc = EVP_PKEY_fromdata(ctx, &(new_key->key), EVP_PKEY_PUBLIC_KEY, params);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_free(params);
+ return -1;
+ }
+
+ OSSL_PARAM_free(params);
+ EVP_PKEY_CTX_free(ctx);
+
+ return SSH_OK;
+}
+
+int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote)
+{
+ return evp_dup_pkey("RSA", key, demote, new_key);
+}
+
+int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote)
+{
+ return evp_dup_pkey("EC", key, demote, new_key);
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+
+ssh_string
+pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p)
+{
+ ssh_string s = NULL;
+ size_t len;
+
+ len = EC_POINT_point2oct(g,
+ p,
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL,
+ 0,
+ NULL);
+ if (len == 0) {
+ return NULL;
+ }
+
+ s = ssh_string_new(len);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = EC_POINT_point2oct(g,
+ p,
+ POINT_CONVERSION_UNCOMPRESSED,
+ ssh_string_data(s),
+ ssh_string_len(s),
+ NULL);
+ if (len != ssh_string_len(s)) {
+ SSH_STRING_FREE(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+int pki_key_ecgroup_name_to_nid(const char *group)
+{
+ if (strcmp(group, NISTP256) == 0 ||
+ strcmp(group, "secp256r1") == 0 ||
+ strcmp(group, "prime256v1") == 0) {
+ return NID_X9_62_prime256v1;
+ } else if (strcmp(group, NISTP384) == 0 ||
+ strcmp(group, "secp384r1") == 0) {
+ return NID_secp384r1;
+ } else if (strcmp(group, NISTP521) == 0 ||
+ strcmp(group, "secp521r1") == 0) {
+ return NID_secp521r1;
+ }
+ return -1;
+}
#endif /* LIBCRYPTO */
diff --git a/src/libgcrypt.c b/src/libgcrypt.c
index 2383ffa0..4feda00c 100644
--- a/src/libgcrypt.c
+++ b/src/libgcrypt.c
@@ -69,181 +69,16 @@ static int alloc_key(struct ssh_cipher_struct *cipher) {
void ssh_reseed(void){
}
-int ssh_get_random(void *where, int len, int strong)
-{
- /* variable not used in gcrypt */
- (void) strong;
-
- /* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */
- gcry_randomize(where,len,GCRY_STRONG_RANDOM);
-
- return 1;
-}
-
-SHACTX sha1_init(void) {
- SHACTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA1, 0);
-
- return ctx;
-}
-
-void sha1_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha1(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
-}
-
-#ifdef HAVE_GCRYPT_ECC
-static int nid_to_md_algo(int nid)
-{
- switch (nid) {
- case NID_gcrypt_nistp256:
- return GCRY_MD_SHA256;
- case NID_gcrypt_nistp384:
- return GCRY_MD_SHA384;
- case NID_gcrypt_nistp521:
- return GCRY_MD_SHA512;
- }
- return GCRY_MD_NONE;
-}
-
-void evp(int nid, unsigned char *digest, int len,
- unsigned char *hash, unsigned int *hlen)
-{
- int algo = nid_to_md_algo(nid);
-
- /* Note: What gcrypt calls 'hash' is called 'digest' here and
- vice-versa. */
- gcry_md_hash_buffer(algo, hash, digest, len);
- *hlen = gcry_md_get_algo_dlen(algo);
-}
-
-EVPCTX evp_init(int nid)
-{
- gcry_error_t err;
- int algo = nid_to_md_algo(nid);
- EVPCTX ctx;
-
- err = gcry_md_open(&ctx, algo, 0);
- if (err) {
- return NULL;
- }
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- gcry_md_write(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- int algo = gcry_md_get_algo(ctx);
- *mdlen = gcry_md_get_algo_dlen(algo);
- memcpy(md, gcry_md_read(ctx, algo), *mdlen);
- gcry_md_close(ctx);
-}
-#endif
-
-SHA256CTX sha256_init(void) {
- SHA256CTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA256, 0);
-
- return ctx;
-}
-
-void sha256_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha256_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA256_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha256(const unsigned char *digest, int len, unsigned char *hash){
- gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
-}
-
-SHA384CTX sha384_init(void) {
- SHA384CTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA384, 0);
-
- return ctx;
-}
-
-void sha384_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha384_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA384_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha384(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len);
-}
-
-SHA512CTX sha512_init(void) {
- SHA512CTX ctx = NULL;
- gcry_md_open(&ctx, GCRY_MD_SHA512, 0);
-
- return ctx;
-}
-
-void sha512_update(SHACTX c, const void *data, unsigned long len) {
- gcry_md_write(c, data, len);
-}
-
-void sha512_final(unsigned char *md, SHACTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), SHA512_DIGEST_LEN);
- gcry_md_close(c);
-}
-
-void sha512(const unsigned char *digest, int len, unsigned char *hash) {
- gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len);
-}
-
-MD5CTX md5_init(void) {
- MD5CTX c = NULL;
- gcry_md_open(&c, GCRY_MD_MD5, 0);
-
- return c;
-}
-
-void md5_update(MD5CTX c, const void *data, unsigned long len) {
- gcry_md_write(c,data,len);
-}
-
-void md5_final(unsigned char *md, MD5CTX c) {
- gcry_md_final(c);
- memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN);
- gcry_md_close(c);
-}
-
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
return sshkdf_derive_key(crypto, key, key_len,
key_type, output, requested_len);
}
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
+HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type) {
HMACCTX c = NULL;
switch(type) {
@@ -268,14 +103,17 @@ HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
return c;
}
-void hmac_update(HMACCTX c, const void *data, unsigned long len) {
+int hmac_update(HMACCTX c, const void *data, size_t len) {
gcry_md_write(c, data, len);
+ return 1;
}
-void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len) {
- *len = gcry_md_get_algo_dlen(gcry_md_get_algo(c));
+int hmac_final(HMACCTX c, unsigned char *hashmacbuf, size_t *len) {
+ unsigned int tmp = gcry_md_get_algo_dlen(gcry_md_get_algo(c));
+ *len = (size_t)tmp;
memcpy(hashmacbuf, gcry_md_read(c, 0), *len);
gcry_md_close(c);
+ return 1;
}
#ifdef WITH_BLOWFISH_CIPHER
@@ -307,12 +145,12 @@ static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *I
}
static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
#endif /* WITH_BLOWFISH_CIPHER */
@@ -350,7 +188,7 @@ static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
}
break;
default:
- SSH_LOG(SSH_LOG_WARNING, "Unksupported key length %u.", cipher->keysize);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported key length %u.", cipher->keysize);
SAFE_FREE(cipher->key);
return -1;
}
@@ -434,7 +272,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setiv(cipher->key[0],
cipher->last_iv,
AES_GCM_IVLEN);
- /* This actualy does not increment the packet counter for the
+ /* This actually does not increment the packet counter for the
* current encryption operation, but for the next one. The first
* operation needs to be completed with the derived IV.
*
@@ -443,7 +281,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
*/
uint64_inc(cipher->last_iv + 4);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
return;
}
@@ -451,7 +289,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
/* Pass the authenticated data (packet_length) */
err = gcry_cipher_authenticate(cipher->key[0], in, aadlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_authenticate failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_authenticate failed: %s",
gpg_strerror(err));
return;
}
@@ -464,7 +302,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)in + aadlen,
len - aadlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
return;
}
@@ -474,7 +312,7 @@ aes_gcm_encrypt(struct ssh_cipher_struct *cipher,
(void *)tag,
authlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_gettag failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_gettag failed: %s",
gpg_strerror(err));
return;
}
@@ -499,7 +337,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setiv(cipher->key[0],
cipher->last_iv,
AES_GCM_IVLEN);
- /* This actualy does not increment the packet counter for the
+ /* This actually does not increment the packet counter for the
* current encryption operation, but for the next one. The first
* operation needs to be completed with the derived IV.
*
@@ -508,7 +346,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
*/
uint64_inc(cipher->last_iv + 4);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -518,7 +356,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
complete_packet,
aadlen);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_authenticate failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_authenticate failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -532,7 +370,7 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)complete_packet + aadlen,
encrypted_size);
if (err) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_decrypt failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -542,10 +380,10 @@ aes_gcm_decrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)complete_packet + aadlen + encrypted_size,
authlen);
if (gpg_err_code(err) == GPG_ERR_CHECKSUM) {
- SSH_LOG(SSH_LOG_WARNING, "The authentication tag does not match");
+ SSH_LOG(SSH_LOG_DEBUG, "The authentication tag does not match");
return SSH_ERROR;
} else if (err != GPG_ERR_NO_ERROR) {
- SSH_LOG(SSH_LOG_WARNING, "General error while decryption: %s",
+ SSH_LOG(SSH_LOG_TRACE, "General error while decryption: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -578,12 +416,12 @@ static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
}
static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in,
- void *out, unsigned long len) {
+ void *out, size_t len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
@@ -631,7 +469,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_open(&ctx->main_hd, GCRY_CIPHER_CHACHA20,
GCRY_CIPHER_MODE_STREAM, 0);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_open failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_open failed: %s",
gpg_strerror(err));
SAFE_FREE(cipher->chacha20_schedule);
return -1;
@@ -639,7 +477,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_open(&ctx->header_hd, GCRY_CIPHER_CHACHA20,
GCRY_CIPHER_MODE_STREAM, 0);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_open failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_open failed: %s",
gpg_strerror(err));
gcry_cipher_close(ctx->main_hd);
SAFE_FREE(cipher->chacha20_schedule);
@@ -647,7 +485,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
}
err = gcry_mac_open(&ctx->mac_hd, GCRY_MAC_POLY1305, 0, NULL);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_open failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_open failed: %s",
gpg_strerror(err));
gcry_cipher_close(ctx->main_hd);
gcry_cipher_close(ctx->header_hd);
@@ -660,7 +498,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setkey(ctx->main_hd, u8key, CHACHA20_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setkey failed: %s",
gpg_strerror(err));
chacha20_cleanup(cipher);
return -1;
@@ -669,7 +507,7 @@ static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
err = gcry_cipher_setkey(ctx->header_hd, u8key + CHACHA20_KEYLEN,
CHACHA20_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setkey failed: %s",
gpg_strerror(err));
chacha20_cleanup(cipher);
return -1;
@@ -696,7 +534,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* step 1, prepare the poly1305 key */
err = gcry_cipher_setiv(ctx->main_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
goto out;
}
@@ -708,13 +546,13 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
zero_block,
sizeof(zero_block));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
err = gcry_mac_setkey(ctx->mac_hd, poly_key, POLY1305_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_setkey failed: %s",
gpg_strerror(err));
goto out;
}
@@ -722,7 +560,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* step 2, encrypt length field */
err = gcry_cipher_setiv(ctx->header_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
goto out;
}
@@ -732,7 +570,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)&in_packet->length,
sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
@@ -744,7 +582,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
in_packet->payload,
len - sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
@@ -752,13 +590,13 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* step 4, compute the MAC */
err = gcry_mac_write(ctx->mac_hd, (uint8_t *)out_packet, len);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_write failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_write failed: %s",
gpg_strerror(err));
goto out;
}
err = gcry_mac_read(ctx->mac_hd, tag, &taglen);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_read failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_read failed: %s",
gpg_strerror(err));
goto out;
}
@@ -784,7 +622,7 @@ static int chacha20_poly1305_aead_decrypt_length(
err = gcry_cipher_setiv(ctx->header_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -794,7 +632,7 @@ static int chacha20_poly1305_aead_decrypt_length(
in,
sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_decrypt failed: %s",
gpg_strerror(err));
return SSH_ERROR;
}
@@ -820,7 +658,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
/* step 1, prepare the poly1305 key */
err = gcry_cipher_setiv(ctx->main_hd, (uint8_t *)&seq, sizeof(seq));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_setiv failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_setiv failed: %s",
gpg_strerror(err));
goto out;
}
@@ -832,13 +670,13 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
zero_block,
sizeof(zero_block));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_encrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_encrypt failed: %s",
gpg_strerror(err));
goto out;
}
err = gcry_mac_setkey(ctx->mac_hd, poly_key, POLY1305_KEYLEN);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_setkey failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_setkey failed: %s",
gpg_strerror(err));
goto out;
}
@@ -847,7 +685,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
err = gcry_mac_write(ctx->mac_hd, (uint8_t *)complete_packet,
encrypted_size + sizeof(uint32_t));
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_write failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_write failed: %s",
gpg_strerror(err));
goto out;
}
@@ -856,7 +694,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
SSH_LOG(SSH_LOG_PACKET, "poly1305 verify error");
goto out;
} else if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_mac_verify failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_mac_verify failed: %s",
gpg_strerror(err));
goto out;
}
@@ -868,7 +706,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)complete_packet + sizeof(uint32_t),
encrypted_size);
if (err != 0) {
- SSH_LOG(SSH_LOG_WARNING, "gcry_cipher_decrypt failed: %s",
+ SSH_LOG(SSH_LOG_TRACE, "gcry_cipher_decrypt failed: %s",
gpg_strerror(err));
goto out;
}
diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c
index ee3fad79..8fb36e53 100644
--- a/src/libmbedcrypto.c
+++ b/src/libmbedcrypto.c
@@ -27,6 +27,7 @@
#include "libssh/crypto.h"
#include "libssh/priv.h"
#include "libssh/misc.h"
+#include "mbedcrypto-compat.h"
#if defined(MBEDTLS_CHACHA20_C) && defined(MBEDTLS_POLY1305_C)
#include "libssh/bytearray.h"
#include "libssh/chacha20-poly1305-common.h"
@@ -41,7 +42,7 @@
#endif /* MBEDTLS_GCM_C */
static mbedtls_entropy_context ssh_mbedtls_entropy;
-static mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg;
+extern mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg;
static int libmbedcrypto_initialized = 0;
@@ -50,354 +51,16 @@ void ssh_reseed(void)
mbedtls_ctr_drbg_reseed(&ssh_mbedtls_ctr_drbg, NULL, 0);
}
-int ssh_get_random(void *where, int len, int strong)
-{
- return ssh_mbedtls_random(where, len, strong);
-}
-
-SHACTX sha1_init(void)
-{
- SHACTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha1_update(SHACTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha1_final(unsigned char *md, SHACTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha1(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-static mbedtls_md_type_t nid_to_md_algo(int nid)
-{
- switch (nid) {
- case NID_mbedtls_nistp256:
- return MBEDTLS_MD_SHA256;
- case NID_mbedtls_nistp384:
- return MBEDTLS_MD_SHA384;
- case NID_mbedtls_nistp521:
- return MBEDTLS_MD_SHA512;
- }
- return MBEDTLS_MD_NONE;
-}
-
-void evp(int nid, unsigned char *digest, int len,
- unsigned char *hash, unsigned int *hlen)
-{
- mbedtls_md_type_t algo = nid_to_md_algo(nid);
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(algo);
-
-
- if (md_info != NULL) {
- *hlen = mbedtls_md_get_size(md_info);
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-EVPCTX evp_init(int nid)
-{
- EVPCTX ctx = NULL;
- int rc;
- mbedtls_md_type_t algo = nid_to_md_algo(nid);
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(algo);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void evp_update(EVPCTX ctx, const void *data, unsigned long len)
-{
- mbedtls_md_update(ctx, data, len);
-}
-
-void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
-{
- *mdlen = mbedtls_md_get_size(ctx->md_info);
- mbedtls_md_finish(ctx, md);
- mbedtls_md_free(ctx);
- SAFE_FREE(ctx);
-}
-
-SHA256CTX sha256_init(void)
-{
- SHA256CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if(ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha256_update(SHA256CTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha256_final(unsigned char *md, SHA256CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha256(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-SHA384CTX sha384_init(void)
-{
- SHA384CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
-
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha384_update(SHA384CTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha384_final(unsigned char *md, SHA384CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha384(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-SHA512CTX sha512_init(void)
-{
- SHA512CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-void sha512_update(SHA512CTX c, const void *data, unsigned long len)
-{
- mbedtls_md_update(c, data, len);
-}
-
-void sha512_final(unsigned char *md, SHA512CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
-void sha512(const unsigned char *digest, int len, unsigned char *hash)
-{
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
- if (md_info != NULL) {
- mbedtls_md(md_info, digest, len, hash);
- }
-}
-
-MD5CTX md5_init(void)
-{
- MD5CTX ctx = NULL;
- int rc;
- const mbedtls_md_info_t *md_info =
- mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
- if (md_info == NULL) {
- return NULL;
- }
-
- ctx = malloc(sizeof(mbedtls_md_context_t));
- if (ctx == NULL) {
- return NULL;
- }
-
- mbedtls_md_init(ctx);
-
- rc = mbedtls_md_setup(ctx, md_info, 0);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- rc = mbedtls_md_starts(ctx);
- if (rc != 0) {
- SAFE_FREE(ctx);
- return NULL;
- }
-
- return ctx;
-}
-
-
-void md5_update(MD5CTX c, const void *data, unsigned long len) {
- mbedtls_md_update(c, data, len);
-}
-
-void md5_final(unsigned char *md, MD5CTX c)
-{
- mbedtls_md_finish(c, md);
- mbedtls_md_free(c);
- SAFE_FREE(c);
-}
-
int ssh_kdf(struct ssh_crypto_struct *crypto,
unsigned char *key, size_t key_len,
- int key_type, unsigned char *output,
+ uint8_t key_type, unsigned char *output,
size_t requested_len)
{
return sshkdf_derive_key(crypto, key, key_len,
key_type, output, requested_len);
}
-HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type)
+HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type)
{
HMACCTX ctx = NULL;
const mbedtls_md_info_t *md_info = NULL;
@@ -446,17 +109,21 @@ error:
return NULL;
}
-void hmac_update(HMACCTX c, const void *data, unsigned long len)
+/* mbedtls returns 0 on success, but in this context
+ * success is 1 */
+int hmac_update(HMACCTX c, const void *data, size_t len)
{
- mbedtls_md_hmac_update(c, data, len);
+ return !mbedtls_md_hmac_update(c, data, len);
}
-void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len)
+int hmac_final(HMACCTX c, unsigned char *hashmacbuf, size_t *len)
{
- *len = mbedtls_md_get_size(c->md_info);
- mbedtls_md_hmac_finish(c, hashmacbuf);
+ int rc;
+ *len = (unsigned int)mbedtls_md_get_size(c->MBEDTLS_PRIVATE(md_info));
+ rc = !mbedtls_md_hmac_finish(c, hashmacbuf);
mbedtls_md_free(c);
SAFE_FREE(c);
+ return rc;
}
static int
@@ -467,6 +134,8 @@ cipher_init(struct ssh_cipher_struct *cipher,
{
const mbedtls_cipher_info_t *cipher_info = NULL;
mbedtls_cipher_context_t *ctx;
+ size_t key_bitlen = 0;
+ size_t iv_size = 0;
int rc;
if (operation == MBEDTLS_ENCRYPT) {
@@ -474,7 +143,7 @@ cipher_init(struct ssh_cipher_struct *cipher,
} else if (operation == MBEDTLS_DECRYPT) {
ctx = &cipher->decrypt_ctx;
} else {
- SSH_LOG(SSH_LOG_WARNING, "unknown operation");
+ SSH_LOG(SSH_LOG_TRACE, "unknown operation");
return 1;
}
@@ -483,21 +152,21 @@ cipher_init(struct ssh_cipher_struct *cipher,
rc = mbedtls_cipher_setup(ctx, cipher_info);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setup failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_setup failed");
goto error;
}
- rc = mbedtls_cipher_setkey(ctx, key,
- cipher_info->key_bitlen,
- operation);
+ key_bitlen = mbedtls_cipher_info_get_key_bitlen(cipher_info);
+ rc = mbedtls_cipher_setkey(ctx, key, key_bitlen, operation);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_setkey failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_setkey failed");
goto error;
}
- rc = mbedtls_cipher_set_iv(ctx, IV, cipher_info->iv_size);
+ iv_size = mbedtls_cipher_info_get_iv_size(cipher_info);
+ rc = mbedtls_cipher_set_iv(ctx, IV, iv_size);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_iv failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_set_iv failed");
goto error;
}
@@ -516,13 +185,13 @@ cipher_set_encrypt_key(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_ENCRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -540,23 +209,23 @@ cipher_set_encrypt_key_cbc(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_ENCRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
- /* libssh only encypts and decrypts packets that are multiples of a block
+ /* libssh only encrypts and decrypts packets that are multiples of a block
* size, and no padding is used */
rc = mbedtls_cipher_set_padding_mode(&cipher->encrypt_ctx,
MBEDTLS_PADDING_NONE);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_padding_mode failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_set_padding_mode failed");
goto error;
}
rc = mbedtls_cipher_reset(&cipher->encrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -573,17 +242,18 @@ cipher_set_key_gcm(struct ssh_cipher_struct *cipher,
void *IV)
{
const mbedtls_cipher_info_t *cipher_info = NULL;
+ size_t key_bitlen = 0;
int rc;
mbedtls_gcm_init(&cipher->gcm_ctx);
cipher_info = mbedtls_cipher_info_from_type(cipher->type);
- rc = mbedtls_gcm_setkey(&cipher->gcm_ctx,
- MBEDTLS_CIPHER_ID_AES,
- key,
- cipher_info->key_bitlen);
+ key_bitlen = mbedtls_cipher_info_get_key_bitlen(cipher_info);
+ rc = mbedtls_gcm_setkey(&cipher->gcm_ctx, MBEDTLS_CIPHER_ID_AES,
+ key, key_bitlen);
+
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_setkey failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_gcm_setkey failed");
goto error;
}
@@ -606,13 +276,13 @@ cipher_set_decrypt_key(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_DECRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
mbedtls_cipher_reset(&cipher->decrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -631,20 +301,20 @@ cipher_set_decrypt_key_cbc(struct ssh_cipher_struct *cipher,
rc = cipher_init(cipher, MBEDTLS_DECRYPT, key, IV);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "cipher_init failed");
+ SSH_LOG(SSH_LOG_TRACE, "cipher_init failed");
goto error;
}
rc = mbedtls_cipher_set_padding_mode(&cipher->decrypt_ctx,
MBEDTLS_PADDING_NONE);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_set_padding_mode failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_set_padding_mode failed");
goto error;
}
mbedtls_cipher_reset(&cipher->decrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed");
goto error;
}
@@ -664,7 +334,7 @@ static void cipher_encrypt(struct ssh_cipher_struct *cipher,
int rc = 0;
rc = mbedtls_cipher_update(&cipher->encrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during encryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during encryption");
return;
}
@@ -680,12 +350,12 @@ static void cipher_encrypt(struct ssh_cipher_struct *cipher,
total_len += outlen;
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_finish failed during encryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_finish failed during encryption");
return;
}
if (total_len != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -693,18 +363,18 @@ static void cipher_encrypt(struct ssh_cipher_struct *cipher,
}
static void cipher_encrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void *out,
- unsigned long len)
+ size_t len)
{
size_t outlen = 0;
int rc = 0;
rc = mbedtls_cipher_update(&cipher->encrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during encryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during encryption");
return;
}
if (outlen != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -722,7 +392,7 @@ static void cipher_decrypt(struct ssh_cipher_struct *cipher,
rc = mbedtls_cipher_update(&cipher->decrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during decryption");
return;
}
@@ -736,14 +406,14 @@ static void cipher_decrypt(struct ssh_cipher_struct *cipher,
outlen, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed during decryption");
return;
}
total_len += outlen;
if (total_len != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -751,13 +421,13 @@ static void cipher_decrypt(struct ssh_cipher_struct *cipher,
}
static void cipher_decrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void *out,
- unsigned long len)
+ size_t len)
{
size_t outlen = 0;
int rc = 0;
rc = mbedtls_cipher_update(&cipher->decrypt_ctx, in, len, out, &outlen);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_update failed during decryption");
return;
}
@@ -776,19 +446,19 @@ static void cipher_decrypt_cbc(struct ssh_cipher_struct *cipher, void *in, void
}
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_finish failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_finish failed during decryption");
return;
}
rc = mbedtls_cipher_reset(&cipher->decrypt_ctx);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_reset failed during decryption");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_cipher_reset failed during decryption");
return;
}
if (outlen != len) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_cipher_update: output size %zu for %zu",
+ SSH_LOG(SSH_LOG_DEBUG, "mbedtls_cipher_update: output size %zu for %zu",
outlen, len);
return;
}
@@ -842,7 +512,7 @@ cipher_encrypt_gcm(struct ssh_cipher_struct *cipher,
authlen,
tag); /* tag */
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_crypt_and_tag failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_gcm_crypt_and_tag failed");
return;
}
@@ -876,7 +546,7 @@ cipher_decrypt_gcm(struct ssh_cipher_struct *cipher,
(const uint8_t *)complete_packet + aadlen, /* input */
(unsigned char *)out); /* output */
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_gcm_auth_decrypt failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_gcm_auth_decrypt failed");
return SSH_ERROR;
}
@@ -950,14 +620,14 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
/* K2 uses the first half of the key */
rv = mbedtls_chacha20_setkey(&ctx->main_ctx, u8key);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_setkey(main_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_setkey(main_ctx) failed");
goto out;
}
/* K1 uses the second half of the key */
rv = mbedtls_chacha20_setkey(&ctx->header_ctx, u8key + CHACHA20_KEYLEN);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_setkey(header_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_setkey(header_ctx) failed");
goto out;
}
@@ -981,7 +651,7 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
/* The nonce in mbedTLS is 96 b long. The counter is passed through separate
* parameter of 32 b size.
- * Encode the seqence number into the last 8 bytes.
+ * Encode the sequence number into the last 8 bytes.
*/
PUSH_BE_U64(seqbuf, 4, seq);
#ifdef DEBUG_CRYPTO
@@ -990,13 +660,13 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
ret = mbedtls_chacha20_starts(&ctx->header_ctx, seqbuf, 0);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_starts(header_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_starts(header_ctx) failed");
return SSH_ERROR;
}
- ret = mbedtls_chacha20_starts(&ctx->header_ctx, seqbuf, 0);
+ ret = mbedtls_chacha20_starts(&ctx->main_ctx, seqbuf, 0);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_starts(header_ctx) failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_starts(main_ctx) failed");
return SSH_ERROR;
}
@@ -1025,7 +695,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
rv = mbedtls_chacha20_update(&ctx->main_ctx, sizeof(zero_block),
zero_block, poly_key);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
goto out;
}
#ifdef DEBUG_CRYPTO
@@ -1035,7 +705,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
/* Set the Poly1305 key */
rv = mbedtls_poly1305_starts(&ctx->poly_ctx, poly_key);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_starts failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_starts failed");
goto out;
}
@@ -1071,7 +741,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
rv = mbedtls_chacha20_update(&ctx->header_ctx, sizeof(uint32_t), in, out);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
return SSH_ERROR;
}
@@ -1099,7 +769,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
rv = chacha20_poly1305_packet_setup(cipher, seq, 0);
if (rv != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
goto out;
}
@@ -1111,13 +781,13 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
rv = mbedtls_poly1305_update(&ctx->poly_ctx, complete_packet,
encrypted_size + sizeof(uint32_t));
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_update failed");
goto out;
}
rv = mbedtls_poly1305_finish(&ctx->poly_ctx, tag);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_finish failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_finish failed");
goto out;
}
@@ -1126,7 +796,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */
/* Verify the calculated MAC matches the attached MAC */
- cmp = memcmp(tag, mac, POLY1305_TAGLEN);
+ cmp = secure_memcmp(tag, mac, POLY1305_TAGLEN);
if (cmp != 0) {
/* mac error */
SSH_LOG(SSH_LOG_PACKET, "poly1305 verify error");
@@ -1138,7 +808,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
(uint8_t *)complete_packet + sizeof(uint32_t),
out);
if (rv != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
goto out;
}
@@ -1162,7 +832,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* Prepare the Poly1305 key */
ret = chacha20_poly1305_packet_setup(cipher, seq, 1);
if (ret != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet");
return;
}
@@ -1175,7 +845,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
(unsigned char *)&in_packet->length,
(unsigned char *)&out_packet->length);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
return;
}
#ifdef DEBUG_CRYPTO
@@ -1187,20 +857,20 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
/* We already did encrypt one block so the counter should be in the correct position */
ret = mbedtls_chacha20_update(&ctx->main_ctx, len - sizeof(uint32_t),
in_packet->payload, out_packet->payload);
- if (ret != 1) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_chacha20_update failed");
+ if (ret != 0) {
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_chacha20_update failed");
return;
}
/* step 4, compute the MAC */
ret = mbedtls_poly1305_update(&ctx->poly_ctx, (const unsigned char *)out_packet, len);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_update failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_update failed");
return;
}
ret = mbedtls_poly1305_finish(&ctx->poly_ctx, tag);
if (ret != 0) {
- SSH_LOG(SSH_LOG_WARNING, "mbedtls_poly1305_finish failed");
+ SSH_LOG(SSH_LOG_TRACE, "mbedtls_poly1305_finish failed");
return;
}
}
@@ -1411,7 +1081,7 @@ int ssh_crypto_init(void)
mbedtls_ctr_drbg_free(&ssh_mbedtls_ctr_drbg);
}
-#if defined(MBEDTLS_CHACHA20_C) && defined(MBEDTLS_POLY1305_C)
+#if !(defined(MBEDTLS_CHACHA20_C) && defined(MBEDTLS_POLY1305_C))
for (i = 0; ssh_ciphertab[i].name != NULL; i++) {
int cmp;
@@ -1430,22 +1100,6 @@ int ssh_crypto_init(void)
return SSH_OK;
}
-int ssh_mbedtls_random(void *where, int len, int strong)
-{
- int rc = 0;
- if (strong) {
- mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
- MBEDTLS_CTR_DRBG_PR_ON);
- rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
- mbedtls_ctr_drbg_set_prediction_resistance(&ssh_mbedtls_ctr_drbg,
- MBEDTLS_CTR_DRBG_PR_OFF);
- } else {
- rc = mbedtls_ctr_drbg_random(&ssh_mbedtls_ctr_drbg, where, len);
- }
-
- return !rc;
-}
-
mbedtls_ctr_drbg_context *ssh_get_mbedtls_ctr_drbg_context(void)
{
return &ssh_mbedtls_ctr_drbg;
diff --git a/src/libssh.map b/src/libssh.map
index c9bedee0..558f921d 100644
--- a/src/libssh.map
+++ b/src/libssh.map
@@ -1,4 +1,4 @@
-# This map file was updated with abimap-0.3.1
+# This map file was updated with abimap-0.3.2
LIBSSH_4_5_0 # Released
{
@@ -447,3 +447,30 @@ LIBSSH_4_8_1 # Released
ssh_session_get_known_hosts_entry;
ssh_threads_get_default;
} LIBSSH_4_8_0;
+
+LIBSSH_4_9_0 # Released
+{
+ global:
+ ssh_channel_open_forward_port;
+ ssh_key_dup;
+ ssh_send_issue_banner;
+ ssh_session_set_disconnect_message;
+ ssh_userauth_publickey_auto_get_current_identity;
+ ssh_vlog;
+} LIBSSH_4_8_1;
+
+LIBSSH_AFTER_4_9_0
+{
+ global:
+ sftp_channel_default_data_callback;
+ sftp_channel_default_subsystem_request;
+ sftp_aio_begin_read;
+ sftp_aio_begin_write;
+ sftp_aio_free;
+ sftp_aio_wait_read;
+ sftp_aio_wait_write;
+ ssh_pki_export_privkey_base64_format;
+ ssh_pki_export_privkey_file_format;
+ ssh_channel_request_pty_size_modes;
+} LIBSSH_4_9_0;
+
diff --git a/src/log.c b/src/log.c
index a8664b16..5bae18b8 100644
--- a/src/log.c
+++ b/src/log.c
@@ -38,12 +38,16 @@
#include "libssh/misc.h"
#include "libssh/session.h"
+#ifndef LOG_SIZE
+#define LOG_SIZE 1024
+#endif
+
static LIBSSH_THREAD int ssh_log_level;
static LIBSSH_THREAD ssh_logging_callback ssh_log_cb;
static LIBSSH_THREAD void *ssh_log_userdata;
/**
- * @defgroup libssh_log The SSH logging functions.
+ * @defgroup libssh_log The SSH logging functions
* @ingroup libssh
*
* Logging functions for debugging and problem resolving.
@@ -94,38 +98,52 @@ static void ssh_log_stderr(int verbosity,
fprintf(stderr, " %s\n", buffer);
}
+static void ssh_log_custom(ssh_logging_callback log_fn,
+ int verbosity,
+ const char *function,
+ const char *buffer)
+{
+ char buf[LOG_SIZE + 64];
+
+ snprintf(buf, sizeof(buf), "%s: %s", function, buffer);
+ log_fn(verbosity, function, buf, ssh_get_log_userdata());
+}
+
void ssh_log_function(int verbosity,
const char *function,
const char *buffer)
{
ssh_logging_callback log_fn = ssh_get_log_callback();
- if (log_fn) {
- char buf[1024];
- snprintf(buf, sizeof(buf), "%s: %s", function, buffer);
-
- log_fn(verbosity,
- function,
- buf,
- ssh_get_log_userdata());
+ if (log_fn) {
+ ssh_log_custom(log_fn, verbosity, function, buffer);
return;
}
ssh_log_stderr(verbosity, function, buffer);
}
+void ssh_vlog(int verbosity,
+ const char *function,
+ const char *format,
+ va_list *va)
+{
+ char buffer[LOG_SIZE];
+
+ vsnprintf(buffer, sizeof(buffer), format, *va);
+ ssh_log_function(verbosity, function, buffer);
+}
+
void _ssh_log(int verbosity,
const char *function,
const char *format, ...)
{
- char buffer[1024];
va_list va;
if (verbosity <= ssh_get_log_level()) {
va_start(va, format);
- vsnprintf(buffer, sizeof(buffer), format, va);
+ ssh_vlog(verbosity, function, format, &va);
va_end(va);
- ssh_log_function(verbosity, function, buffer);
}
}
@@ -135,14 +153,12 @@ void ssh_log(ssh_session session,
int verbosity,
const char *format, ...)
{
- char buffer[1024];
va_list va;
if (verbosity <= session->common.log_verbosity) {
va_start(va, format);
- vsnprintf(buffer, sizeof(buffer), format, va);
+ ssh_vlog(verbosity, "", format, &va);
va_end(va);
- ssh_log_function(verbosity, "", buffer);
}
}
@@ -157,14 +173,12 @@ void ssh_log_common(struct ssh_common_struct *common,
const char *function,
const char *format, ...)
{
- char buffer[1024];
va_list va;
if (verbosity <= common->log_verbosity) {
va_start(va, format);
- vsnprintf(buffer, sizeof(buffer), format, va);
+ ssh_vlog(verbosity, function, format, &va);
va_end(va);
- ssh_log_function(verbosity, function, buffer);
}
}
diff --git a/src/match.c b/src/match.c
index 1a60d732..3e58f733 100644
--- a/src/match.c
+++ b/src/match.c
@@ -43,7 +43,7 @@
#include "libssh/priv.h"
-#define MAX_MATCH_RECURSION 32
+#define MAX_MATCH_RECURSION 16
/*
* Returns true if the given string matches the pattern (which may contain ?
@@ -51,74 +51,77 @@
*/
static int match_pattern(const char *s, const char *pattern, size_t limit)
{
- bool had_asterisk = false;
- if (s == NULL || pattern == NULL || limit <= 0) {
- return 0;
- }
+ bool had_asterisk = false;
- for (;;) {
- /* If at end of pattern, accept if also at end of string. */
- if (*pattern == '\0') {
- return (*s == '\0');
+ if (s == NULL || pattern == NULL || limit <= 0) {
+ return 0;
}
- while (*pattern == '*') {
- /* Skip the asterisk. */
- had_asterisk = true;
- pattern++;
- }
+ for (;;) {
+ /* If at end of pattern, accept if also at end of string. */
+ if (*pattern == '\0') {
+ return (*s == '\0');
+ }
- if (had_asterisk) {
- /* If at end of pattern, accept immediately. */
- if (!*pattern)
- return 1;
+ /* Skip all the asterisks and adjacent question marks */
+ while (*pattern == '*' || (had_asterisk && *pattern == '?')) {
+ if (*pattern == '*') {
+ had_asterisk = true;
+ }
+ pattern++;
+ }
- /* If next character in pattern is known, optimize. */
- if (*pattern != '?') {
+ if (had_asterisk) {
+ /* If at end of pattern, accept immediately. */
+ if (!*pattern)
+ return 1;
+
+ /* If next character in pattern is known, optimize. */
+ if (*pattern != '?') {
+ /*
+ * Look instances of the next character in
+ * pattern, and try to match starting from
+ * those.
+ */
+ for (; *s; s++)
+ if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) {
+ return 1;
+ }
+ /* Failed. */
+ return 0;
+ }
+ /*
+ * Move ahead one character at a time and try to
+ * match at each position.
+ */
+ for (; *s; s++) {
+ if (match_pattern(s, pattern, limit - 1)) {
+ return 1;
+ }
+ }
+ /* Failed. */
+ return 0;
+ }
/*
- * Look instances of the next character in
- * pattern, and try to match starting from
- * those.
+ * There must be at least one more character in the string.
+ * If we are at the end, fail.
*/
- for (; *s; s++)
- if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) {
- return 1;
- }
- /* Failed. */
- return 0;
- }
- /*
- * Move ahead one character at a time and try to
- * match at each position.
- */
- for (; *s; s++) {
- if (match_pattern(s, pattern, limit - 1)) {
- return 1;
+ if (!*s) {
+ return 0;
}
- }
- /* Failed. */
- return 0;
- }
- /*
- * There must be at least one more character in the string.
- * If we are at the end, fail.
- */
- if (!*s) {
- return 0;
- }
- /* Check if the next character of the string is acceptable. */
- if (*pattern != '?' && *pattern != *s) {
- return 0;
- }
+ /* Check if the next character of the string is acceptable. */
+ if (*pattern != '?' && *pattern != *s) {
+ return 0;
+ }
- /* Move to the next character, both in string and in pattern. */
- s++;
- pattern++;
- }
+ /* Move to the next character, both in string and in pattern. */
+ s++;
+ pattern++;
+ }
- /* NOTREACHED */
- return 0;
+ /* NOTREACHED */
+ return 0;
}
/*
@@ -128,11 +131,11 @@ static int match_pattern(const char *s, const char *pattern, size_t limit)
* no match at all.
*/
int match_pattern_list(const char *string, const char *pattern,
- unsigned int len, int dolower) {
+ size_t len, int dolower) {
char sub[1024];
int negated;
int got_positive;
- unsigned int i, subi;
+ size_t i, subi;
got_positive = 0;
for (i = 0; i < len;) {
diff --git a/src/mbedcrypto-compat.h b/src/mbedcrypto-compat.h
new file mode 100644
index 00000000..705294a7
--- /dev/null
+++ b/src/mbedcrypto-compat.h
@@ -0,0 +1,39 @@
+#ifndef MBEDCRYPTO_COMPAT_H
+#define MBEDCRYPTO_COMPAT_H
+
+/* mbedtls/version.h should be available for both v2 and v3
+ * v3 defines the version inside build_info.h so if it isn't defined
+ * in version.h we should have v3
+ */
+#include <mbedtls/version.h>
+#include <mbedtls/cipher.h>
+#ifdef MBEDTLS_VERSION_MAJOR
+#if MBEDTLS_VERSION_MAJOR < 3
+
+static inline size_t mbedtls_cipher_info_get_key_bitlen(
+ const mbedtls_cipher_info_t *info)
+{
+ if (info == NULL) {
+ return 0;
+ }
+ return info->key_bitlen;
+}
+
+static inline size_t mbedtls_cipher_info_get_iv_size(
+ const mbedtls_cipher_info_t *info)
+{
+ if (info == NULL) {
+ return 0;
+ }
+ return (size_t)info->iv_size;
+}
+
+#define MBEDTLS_PRIVATE(X) X
+#endif /* MBEDTLS_VERSION_MAJOR < 3 */
+#else /* MBEDTLS_VERSION_MAJOR */
+#include <mbedtls/build_info.h>
+#if MBEDTLS_VERSION_MAJOR < 3
+#define MBEDTLS_PRIVATE(X) X
+#endif /* MBEDTLS_VERSION_MAJOR < 3 */
+#endif /* MBEDTLS_VERSION_MAJOR */
+#endif /* MBEDCRYPTO_COMPAT_H */
diff --git a/src/mbedcrypto_missing.c b/src/mbedcrypto_missing.c
index ac0d688c..2c1a8d7a 100644
--- a/src/mbedcrypto_missing.c
+++ b/src/mbedcrypto_missing.c
@@ -45,7 +45,7 @@ void ssh_mbedcry_bn_free(bignum bn)
SAFE_FREE(bn);
}
-unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix)
+char *ssh_mbedcry_bn2num(const_bignum num, int radix)
{
char *buf = NULL;
size_t olen;
@@ -56,7 +56,7 @@ unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix)
return NULL;
}
- buf = malloc(olen);
+ buf = mbedtls_calloc(1, olen);
if (buf == NULL) {
return NULL;
}
@@ -67,7 +67,7 @@ unsigned char *ssh_mbedcry_bn2num(const_bignum num, int radix)
return NULL;
}
- return (unsigned char *) buf;
+ return buf;
}
int ssh_mbedcry_rand(bignum rnd, int bits, int top, int bottom)
diff --git a/src/md_crypto.c b/src/md_crypto.c
new file mode 100644
index 00000000..f7cda8dd
--- /dev/null
+++ b/src/md_crypto.c
@@ -0,0 +1,324 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libcrypto-compat.h"
+#include "libssh/crypto.h"
+#include "libssh/wrapper.h"
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+
+SHACTX
+sha1_init(void)
+{
+ int rc;
+ SHACTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha1_ctx_free(SHACTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha1_update(SHACTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1_final(unsigned char *md, SHACTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHACTX c = sha1_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha1_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha1_final(hash, c);
+}
+
+SHA256CTX
+sha256_init(void)
+{
+ int rc;
+ SHA256CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha256_update(SHA256CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256_final(unsigned char *md, SHA256CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHA256CTX c = sha256_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha256_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha256_final(hash, c);
+}
+
+SHA384CTX
+sha384_init(void)
+{
+ int rc;
+ SHA384CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha384_update(SHA384CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384_final(unsigned char *md, SHA384CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHA384CTX c = sha384_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha384_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha384_final(hash, c);
+}
+
+SHA512CTX
+sha512_init(void)
+{
+ int rc = 0;
+ SHA512CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+sha512_update(SHA512CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512_final(unsigned char *md, SHA512CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ SHA512CTX c = sha512_init();
+ int rc;
+
+ if (c == NULL) {
+ return SSH_ERROR;
+ }
+ rc = sha512_update(c, digest, len);
+ if (rc != SSH_OK) {
+ EVP_MD_CTX_free(c);
+ return SSH_ERROR;
+ }
+ return sha512_final(hash, c);
+}
+
+MD5CTX
+md5_init(void)
+{
+ int rc;
+ MD5CTX c = EVP_MD_CTX_new();
+ if (c == NULL) {
+ return NULL;
+ }
+ rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
+ if (rc == 0) {
+ EVP_MD_CTX_free(c);
+ c = NULL;
+ }
+ return c;
+}
+
+void
+md5_ctx_free(MD5CTX c)
+{
+ EVP_MD_CTX_free(c);
+}
+
+int
+md5_update(MD5CTX c, const void *data, size_t len)
+{
+ int rc = EVP_DigestUpdate(c, data, len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+md5_final(unsigned char *md, MD5CTX c)
+{
+ unsigned int mdlen = 0;
+ int rc = EVP_DigestFinal(c, md, &mdlen);
+
+ EVP_MD_CTX_free(c);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
diff --git a/src/md_gcrypt.c b/src/md_gcrypt.c
new file mode 100644
index 00000000..93c7b0d9
--- /dev/null
+++ b/src/md_gcrypt.c
@@ -0,0 +1,246 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/crypto.h"
+#include "libssh/wrapper.h"
+
+#include <gcrypt.h>
+
+SHACTX
+sha1_init(void)
+{
+ SHACTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA1, 0);
+
+ return ctx;
+}
+
+int
+sha1_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+void
+sha1_ctx_free(SHACTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha1_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha1(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
+ return SSH_OK;
+}
+
+SHA256CTX
+sha256_init(void)
+{
+ SHA256CTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA256, 0);
+
+ return ctx;
+}
+
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha256_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+sha256_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA256_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha256(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
+ return SSH_OK;
+}
+
+SHA384CTX
+sha384_init(void)
+{
+ SHA384CTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA384, 0);
+
+ return ctx;
+}
+
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha384_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+sha384_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA384_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha384(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len);
+ return SSH_OK;
+}
+
+SHA512CTX
+sha512_init(void)
+{
+ SHA512CTX ctx = NULL;
+ gcry_md_open(&ctx, GCRY_MD_SHA512, 0);
+
+ return ctx;
+}
+
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+sha512_update(SHACTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+sha512_final(unsigned char *md, SHACTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, SHA512_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
+
+int
+sha512(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len);
+ return SSH_OK;
+}
+
+MD5CTX
+md5_init(void)
+{
+ MD5CTX c = NULL;
+ gcry_md_open(&c, GCRY_MD_MD5, 0);
+
+ return c;
+}
+
+void
+md5_ctx_free(MD5CTX c)
+{
+ gcry_md_close(c);
+}
+
+int
+md5_update(MD5CTX c, const void *data, size_t len)
+{
+ gcry_md_write(c, data, len);
+ return SSH_OK;
+}
+
+int
+md5_final(unsigned char *md, MD5CTX c)
+{
+ unsigned char *tmp = NULL;
+
+ gcry_md_final(c);
+ tmp = gcry_md_read(c, 0);
+ if (tmp == NULL) {
+ gcry_md_close(c);
+ return SSH_ERROR;
+ }
+ memcpy(md, tmp, MD5_DIGEST_LEN);
+ gcry_md_close(c);
+ return SSH_OK;
+}
diff --git a/src/md_mbedcrypto.c b/src/md_mbedcrypto.c
new file mode 100644
index 00000000..b3529b4b
--- /dev/null
+++ b/src/md_mbedcrypto.c
@@ -0,0 +1,407 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2017 Sartura d.o.o.
+ *
+ * Author: Juraj Vijtiuk <juraj.vijtiuk@sartura.hr>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/crypto.h"
+#include "libssh/wrapper.h"
+#include "mbedcrypto-compat.h"
+
+#include <mbedtls/md.h>
+
+SHACTX
+sha1_init(void)
+{
+ SHACTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha1_ctx_free(SHACTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha1_update(SHACTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1_final(unsigned char *md, SHACTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha1_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha1(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+SHA256CTX
+sha256_init(void)
+{
+ SHA256CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha256_ctx_free(SHA256CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha256_update(SHA256CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256_final(unsigned char *md, SHA256CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha256(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+SHA384CTX
+sha384_init(void)
+{
+ SHA384CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha384_ctx_free(SHA384CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha384_update(SHA384CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384_final(unsigned char *md, SHA384CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha384_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha384(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+SHA512CTX
+sha512_init(void)
+{
+ SHA512CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+sha512_ctx_free(SHA512CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+sha512_update(SHA512CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512_final(unsigned char *md, SHA512CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ sha512_ctx_free(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+sha512(const unsigned char *digest, size_t len, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_SHA512);
+ int rc;
+
+ if (md_info == NULL) {
+ return SSH_ERROR;
+ }
+ rc = mbedtls_md(md_info, digest, len, hash);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+MD5CTX
+md5_init(void)
+{
+ MD5CTX ctx = NULL;
+ int rc;
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+ if (md_info == NULL) {
+ return NULL;
+ }
+
+ ctx = malloc(sizeof(mbedtls_md_context_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mbedtls_md_init(ctx);
+
+ rc = mbedtls_md_setup(ctx, md_info, 0);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ rc = mbedtls_md_starts(ctx);
+ if (rc != 0) {
+ SAFE_FREE(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void
+md5_ctx_free(MD5CTX c)
+{
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+}
+
+int
+md5_update(MD5CTX c, const void *data, size_t len)
+{
+ int rc = mbedtls_md_update(c, data, len);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+int
+md5_final(unsigned char *md, MD5CTX c)
+{
+ int rc = mbedtls_md_finish(c, md);
+ mbedtls_md_free(c);
+ SAFE_FREE(c);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
diff --git a/src/messages.c b/src/messages.c
index 25683b23..be9462ac 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -54,7 +54,7 @@
* This file contains the message parsing utilities for client and server
* programs using libssh.
*
- * On the server the the main loop of the program will call
+ * On the server the main loop of the program will call
* ssh_message_get(session) to get messages as they come. They are not 1-1 with
* the protocol messages. Then, the user will know what kind of a message it is
* and use the appropriate functions to handle it (or use the default handlers
@@ -79,7 +79,7 @@ static ssh_message ssh_message_new(ssh_session session)
#ifndef WITH_SERVER
-/* Reduced version of the reply default that only reply with
+/* Reduced version of the reply default that only replies with
* SSH_MSG_UNIMPLEMENTED
*/
static int ssh_message_reply_default(ssh_message msg) {
@@ -160,7 +160,7 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
if (channel != NULL) {
rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to send reply for accepting a channel "
"open");
}
@@ -237,7 +237,7 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
msg->channel_request.pxwidth,
msg->channel_request.pxheight);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Failed to iterate callbacks for window change");
}
return SSH_OK;
@@ -317,6 +317,17 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
return SSH_AGAIN;
}
+static int ssh_reply_channel_open_request(ssh_message msg, ssh_channel channel)
+{
+ if (channel != NULL) {
+ return ssh_message_channel_request_open_reply_accept_channel(msg, channel);
+ }
+
+ ssh_message_reply_default(msg);
+
+ return SSH_OK;
+}
+
static int ssh_execute_client_request(ssh_session session, ssh_message msg)
{
ssh_channel channel = NULL;
@@ -329,30 +340,26 @@ static int ssh_execute_client_request(ssh_session session, ssh_message msg)
msg->channel_request_open.originator,
msg->channel_request_open.originator_port,
session->common.callbacks->userdata);
- if (channel != NULL) {
- rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
- return rc;
- } else {
- ssh_message_reply_default(msg);
- }
-
- return SSH_OK;
+ return ssh_reply_channel_open_request(msg, channel);
} else if (msg->type == SSH_REQUEST_CHANNEL_OPEN
&& msg->channel_request_open.type == SSH_CHANNEL_AUTH_AGENT
&& ssh_callbacks_exists(session->common.callbacks, channel_open_request_auth_agent_function)) {
channel = session->common.callbacks->channel_open_request_auth_agent_function (session,
session->common.callbacks->userdata);
- if (channel != NULL) {
- rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
-
- return rc;
- } else {
- ssh_message_reply_default(msg);
- }
+ return ssh_reply_channel_open_request(msg, channel);
+ } else if (msg->type == SSH_REQUEST_CHANNEL_OPEN
+ && msg->channel_request_open.type == SSH_CHANNEL_FORWARDED_TCPIP
+ && ssh_callbacks_exists(session->common.callbacks, channel_open_request_forwarded_tcpip_function)) {
+ channel = session->common.callbacks->channel_open_request_forwarded_tcpip_function(session,
+ msg->channel_request_open.destination,
+ msg->channel_request_open.destination_port,
+ msg->channel_request_open.originator,
+ msg->channel_request_open.originator_port,
+ session->common.callbacks->userdata);
- return SSH_OK;
+ return ssh_reply_channel_open_request(msg, channel);
}
return rc;
@@ -513,24 +520,30 @@ static int ssh_message_termination(void *s){
* @warning This function blocks until a message has been received. Betterset up
* a callback if this behavior is unwanted.
*/
-ssh_message ssh_message_get(ssh_session session) {
- ssh_message msg = NULL;
- int rc;
+ssh_message ssh_message_get(ssh_session session)
+{
+ ssh_message msg = NULL;
+ int rc;
- msg=ssh_message_pop_head(session);
- if(msg) {
- return msg;
- }
- if(session->ssh_message_list == NULL) {
- session->ssh_message_list = ssh_list_new();
- }
- rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
- ssh_message_termination, session);
- if(rc || session->session_state == SSH_SESSION_STATE_ERROR)
- return NULL;
- msg=ssh_list_pop_head(ssh_message, session->ssh_message_list);
+ msg = ssh_message_pop_head(session);
+ if (msg != NULL) {
+ return msg;
+ }
+ if (session->ssh_message_list == NULL) {
+ session->ssh_message_list = ssh_list_new();
+ if (session->ssh_message_list == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ }
+ rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
+ ssh_message_termination, session);
+ if (rc || session->session_state == SSH_SESSION_STATE_ERROR) {
+ return NULL;
+ }
+ msg = ssh_list_pop_head(ssh_message, session->ssh_message_list);
- return msg;
+ return msg;
}
/**
@@ -587,6 +600,7 @@ void ssh_message_free(ssh_message msg){
switch(msg->type) {
case SSH_REQUEST_AUTH:
SAFE_FREE(msg->auth_request.username);
+ SAFE_FREE(msg->auth_request.sigtype);
if (msg->auth_request.password) {
explicit_bzero(msg->auth_request.password,
strlen(msg->auth_request.password));
@@ -708,8 +722,8 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
rc = ssh_buffer_pack(buffer,
"dPbsssbsS",
- crypto->digest_len, /* session ID string */
- (size_t)crypto->digest_len, crypto->session_id,
+ crypto->session_id_len, /* session ID string */
+ crypto->session_id_len, crypto->session_id,
SSH2_MSG_USERAUTH_REQUEST, /* type */
msg->auth_request.username,
service,
@@ -768,7 +782,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
cmp = strcmp(service, "ssh-connection");
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid service request: %s",
service);
goto end;
@@ -846,6 +860,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
goto error;
}
msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_NONE;
+ msg->auth_request.sigtype = strdup(ssh_string_get_char(algo));
+ if (msg->auth_request.sigtype == NULL) {
+ msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR;
+ SSH_STRING_FREE(algo);
+ algo = NULL;
+ goto error;
+ }
+
// has a valid signature ?
if(has_sign) {
ssh_string sig_blob = NULL;
@@ -1040,7 +1062,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
}
if (session->kbdint == NULL) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a keyboard-interactive "
+ SSH_LOG(SSH_LOG_DEBUG, "Warning: Got a keyboard-interactive "
"response but it seems we didn't send the request.");
session->kbdint = ssh_kbdint_new();
@@ -1061,10 +1083,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
session->kbdint->nanswers = 0;
}
- SSH_LOG(SSH_LOG_PACKET,"kbdint: %d answers",nanswers);
+ SSH_LOG(SSH_LOG_PACKET,"kbdint: %" PRIu32 " answers", nanswers);
if (nanswers > KBDINT_MAX_PROMPT) {
ssh_set_error(session, SSH_FATAL,
- "Too much answers received from client: %u (0x%.4x)",
+ "Too much answers received from client: %" PRIu32 " (0x%.4" PRIx32 ")",
nanswers, nanswers);
ssh_kbdint_free(session->kbdint);
session->kbdint = NULL;
@@ -1074,8 +1096,8 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
if(nanswers != session->kbdint->nprompts) {
/* warn but let the application handle this case */
- SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Number of prompts and answers"
- " mismatch: p=%u a=%u", session->kbdint->nprompts, nanswers);
+ SSH_LOG(SSH_LOG_DEBUG, "Warning: Number of prompts and answers"
+ " mismatch: p=%" PRIu32 " a=%" PRIu32, session->kbdint->nprompts, nanswers);
}
session->kbdint->nanswers = nanswers;
@@ -1154,8 +1176,16 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)");
goto error;
}
-
- if (strcmp(type_c,"session") == 0) {
+
+ if (strcmp(type_c, "session") == 0) {
+ if (session->flags & SSH_SESSION_FLAG_NO_MORE_SESSIONS) {
+ ssh_session_set_disconnect_message(session, "No more sessions allowed!");
+ ssh_set_error(session, SSH_FATAL, "No more sessions allowed!");
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ ssh_disconnect(session);
+ goto error;
+ }
+
msg->channel_request_open.type = SSH_CHANNEL_SESSION;
SAFE_FREE(type_c);
goto end;
@@ -1168,9 +1198,9 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
&destination_port,
&msg->channel_request_open.originator,
&originator_port);
- if (rc != SSH_OK) {
- goto error;
- }
+ if (rc != SSH_OK) {
+ goto error;
+ }
msg->channel_request_open.destination_port = (uint16_t) destination_port;
msg->channel_request_open.originator_port = (uint16_t) originator_port;
@@ -1233,7 +1263,7 @@ end:
*
* @param[in] chan The channel the request is made on.
*
- * @returns SSH_OK on success, SSH_ERROR if an error occured.
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
*/
int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_channel chan) {
ssh_session session;
@@ -1267,7 +1297,7 @@ int ssh_message_channel_request_open_reply_accept_channel(ssh_message msg, ssh_c
}
SSH_LOG(SSH_LOG_PACKET,
- "Accepting a channel request_open for chan %d",
+ "Accepting a channel request_open for chan %" PRIu32,
chan->remote_channel);
rc = ssh_packet_send(session);
@@ -1324,7 +1354,7 @@ ssh_channel ssh_message_channel_request_open_reply_accept(ssh_message msg) {
*
* @param[in] want_reply The want_reply field from the request.
*
- * @returns SSH_OK on success, SSH_ERROR if an error occured.
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
*/
int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet,
const char *request, uint8_t want_reply) {
@@ -1338,7 +1368,7 @@ int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel,
}
SSH_LOG(SSH_LOG_PACKET,
- "Received a %s channel_request for channel (%d:%d) (want_reply=%hhd)",
+ "Received a %s channel_request for channel (%" PRIu32 ":%" PRIu32 ") (want_reply=%hhd)",
request, channel->local_channel, channel->remote_channel, want_reply);
msg->type = SSH_REQUEST_CHANNEL;
@@ -1438,6 +1468,14 @@ error:
return SSH_ERROR;
}
+/** @internal
+ *
+ * @brief Sends a successful channel request reply
+ *
+ * @param msg A message to reply to
+ *
+ * @returns SSH_OK on success, SSH_ERROR if an error occurred.
+ */
int ssh_message_channel_request_reply_success(ssh_message msg) {
uint32_t channel;
int rc;
@@ -1450,7 +1488,7 @@ int ssh_message_channel_request_reply_success(ssh_message msg) {
channel = msg->channel_request.channel->remote_channel;
SSH_LOG(SSH_LOG_PACKET,
- "Sending a channel_request success to channel %d", channel);
+ "Sending a channel_request success to channel %" PRIu32, channel);
rc = ssh_buffer_pack(msg->session->out_buffer,
"bd",
@@ -1481,7 +1519,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
(void)type;
(void)packet;
- SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet");
+ SSH_LOG(SSH_LOG_DEBUG,"Received SSH_MSG_GLOBAL_REQUEST packet");
r = ssh_buffer_unpack(packet, "sb",
&request,
&want_reply);
@@ -1513,12 +1551,12 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD;
msg->global_request.want_reply = want_reply;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
+ SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
msg->global_request.bind_address,
msg->global_request.bind_port);
if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request,
+ SSH_LOG(SSH_LOG_DEBUG, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request,
want_reply, msg->global_request.bind_address,
msg->global_request.bind_port);
session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata);
@@ -1543,7 +1581,7 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD;
msg->global_request.want_reply = want_reply;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
+ SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply,
msg->global_request.bind_address,
msg->global_request.bind_port);
@@ -1557,14 +1595,23 @@ SSH_PACKET_CALLBACK(ssh_packet_global_request){
} else if(strcmp(request, "keepalive@openssh.com") == 0) {
msg->global_request.type = SSH_GLOBAL_REQUEST_KEEPALIVE;
msg->global_request.want_reply = want_reply;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received keepalive@openssh.com %d", want_reply);
+ SSH_LOG(SSH_LOG_DEBUG, "Received keepalive@openssh.com %d", want_reply);
if(ssh_callbacks_exists(session->common.callbacks, global_request_function)) {
session->common.callbacks->global_request_function(session, msg, session->common.callbacks->userdata);
} else {
ssh_message_global_request_reply_success(msg, 0);
}
+ } else if (strcmp(request, "no-more-sessions@openssh.com") == 0) {
+ msg->global_request.type = SSH_GLOBAL_REQUEST_NO_MORE_SESSIONS;
+ msg->global_request.want_reply = want_reply;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Received no-more-sessions@openssh.com %d", want_reply);
+
+ ssh_message_global_request_reply_success(msg, 0);
+
+ session->flags |= SSH_SESSION_FLAG_NO_MORE_SESSIONS;
} else {
- SSH_LOG(SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s, "
+ SSH_LOG(SSH_LOG_DEBUG, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s, "
"want_reply = %d", request, want_reply);
goto reply_with_failure;
}
@@ -1597,7 +1644,7 @@ reply_with_failure:
error:
SAFE_FREE(msg);
SAFE_FREE(request);
- SSH_LOG(SSH_LOG_WARNING, "Invalid SSH_MSG_GLOBAL_REQUEST packet");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid SSH_MSG_GLOBAL_REQUEST packet");
return rc;
}
diff --git a/src/misc.c b/src/misc.c
index 955ceed6..c7a9706c 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <net/if.h>
#endif /* _WIN32 */
@@ -59,6 +60,7 @@
#include <ws2tcpip.h>
#include <shlobj.h>
#include <direct.h>
+#include <netioapi.h>
#ifdef HAVE_IO_H
#include <io.h>
@@ -94,8 +96,10 @@
#define ZLIB_STRING ""
#endif
+#define ARPA_DOMAIN_MAX_LEN 63
+
/**
- * @defgroup libssh_misc The SSH helper functions.
+ * @defgroup libssh_misc The SSH helper functions
* @ingroup libssh
*
* Different helper functions used in the SSH Library.
@@ -104,8 +108,9 @@
*/
#ifdef _WIN32
-char *ssh_get_user_home_dir(void) {
- char tmp[MAX_PATH] = {0};
+char *ssh_get_user_home_dir(void)
+{
+ char tmp[PATH_MAX] = {0};
char *szPath = NULL;
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
@@ -122,12 +127,13 @@ char *ssh_get_user_home_dir(void) {
}
/* we have read access on file */
-int ssh_file_readaccess_ok(const char *file) {
- if (_access(file, 4) < 0) {
- return 0;
- }
+int ssh_file_readaccess_ok(const char *file)
+{
+ if (_access(file, 4) < 0) {
+ return 0;
+ }
- return 1;
+ return 1;
}
/**
@@ -158,7 +164,8 @@ int ssh_dir_writeable(const char *path)
#define SSH_USEC_IN_SEC 1000000LL
#define SSH_SECONDS_SINCE_1601 11644473600LL
-int gettimeofday(struct timeval *__p, void *__t) {
+int ssh_gettimeofday(struct timeval *__p, void *__t)
+{
union {
unsigned long long ns100; /* time since 1 Jan 1601 in 100ns units */
FILETIME ft;
@@ -171,26 +178,34 @@ int gettimeofday(struct timeval *__p, void *__t) {
return (0);
}
-char *ssh_get_local_username(void) {
+char *ssh_get_local_username(void)
+{
DWORD size = 0;
- char *user;
+ char *user = NULL;
+ int rc;
/* get the size */
GetUserName(NULL, &size);
- user = (char *) malloc(size);
+ user = (char *)malloc(size);
if (user == NULL) {
return NULL;
}
if (GetUserName(user, &size)) {
- return user;
+ rc = ssh_check_username_syntax(user);
+ if (rc == SSH_OK) {
+ return user;
+ }
}
+ free(user);
+
return NULL;
}
-int ssh_is_ipaddr_v4(const char *str) {
+int ssh_is_ipaddr_v4(const char *str)
+{
struct sockaddr_storage ss;
int sslen = sizeof(ss);
int rc = SOCKET_ERROR;
@@ -212,24 +227,40 @@ int ssh_is_ipaddr_v4(const char *str) {
return 0;
}
-int ssh_is_ipaddr(const char *str) {
+int ssh_is_ipaddr(const char *str)
+{
int rc = SOCKET_ERROR;
+ char *s = strdup(str);
- if (strchr(str, ':')) {
+ if (s == NULL) {
+ return -1;
+ }
+ if (strchr(s, ':')) {
struct sockaddr_storage ss;
int sslen = sizeof(ss);
-
- /* TODO link-local (IP:v6:addr%ifname). */
- rc = WSAStringToAddressA((LPSTR) str,
+ char *network_interface = strchr(s, '%');
+
+ /* link-local (IP:v6:addr%ifname). */
+ if (network_interface != NULL) {
+ rc = if_nametoindex(network_interface + 1);
+ if (rc == 0) {
+ free(s);
+ return 0;
+ }
+ *network_interface = '\0';
+ }
+ rc = WSAStringToAddressA((LPSTR) s,
AF_INET6,
NULL,
(struct sockaddr*)&ss,
&sslen);
if (rc == 0) {
+ free(s);
return 1;
}
}
+ free(s);
return ssh_is_ipaddr_v4(str);
}
#else /* _WIN32 */
@@ -302,7 +333,7 @@ char *ssh_get_local_username(void)
struct passwd pwd;
struct passwd *pwdbuf = NULL;
char buf[NSS_BUFLEN_PASSWD];
- char *name;
+ char *name = NULL;
int rc;
rc = getpwuid_r(getuid(), &pwd, buf, NSS_BUFLEN_PASSWD, &pwdbuf);
@@ -311,15 +342,18 @@ char *ssh_get_local_username(void)
}
name = strdup(pwd.pw_name);
+ rc = ssh_check_username_syntax(name);
- if (name == NULL) {
+ if (rc != SSH_OK) {
+ free(name);
return NULL;
}
return name;
}
-int ssh_is_ipaddr_v4(const char *str) {
+int ssh_is_ipaddr_v4(const char *str)
+{
int rc = -1;
struct in_addr dest;
@@ -331,25 +365,42 @@ int ssh_is_ipaddr_v4(const char *str) {
return 0;
}
-int ssh_is_ipaddr(const char *str) {
+int ssh_is_ipaddr(const char *str)
+{
int rc = -1;
+ char *s = strdup(str);
- if (strchr(str, ':')) {
+ if (s == NULL) {
+ return -1;
+ }
+ if (strchr(s, ':')) {
struct in6_addr dest6;
-
- /* TODO link-local (IP:v6:addr%ifname). */
- rc = inet_pton(AF_INET6, str, &dest6);
+ char *network_interface = strchr(s, '%');
+
+ /* link-local (IP:v6:addr%ifname). */
+ if (network_interface != NULL) {
+ rc = if_nametoindex(network_interface + 1);
+ if (rc == 0) {
+ free(s);
+ return 0;
+ }
+ *network_interface = '\0';
+ }
+ rc = inet_pton(AF_INET6, s, &dest6);
if (rc > 0) {
+ free(s);
return 1;
}
}
+ free(s);
return ssh_is_ipaddr_v4(str);
}
#endif /* _WIN32 */
-char *ssh_lowercase(const char* str) {
+char *ssh_lowercase(const char* str)
+{
char *new, *p;
if (str == NULL) {
@@ -392,15 +443,17 @@ char *ssh_hostport(const char *host, int port)
* @brief Convert a buffer into a colon separated hex string.
* The caller has to free the memory.
*
- * @param what What should be converted to a hex string.
+ * @param[in] what What should be converted to a hex string.
*
- * @param len Length of the buffer to convert.
+ * @param[in] len Length of the buffer to convert.
*
- * @return The hex string or NULL on error.
+ * @return The hex string or NULL on error. The memory needs
+ * to be freed using ssh_string_free_char().
*
* @see ssh_string_free_char()
*/
-char *ssh_get_hexa(const unsigned char *what, size_t len) {
+char *ssh_get_hexa(const unsigned char *what, size_t len)
+{
const char h[] = "0123456789abcdef";
char *hexa;
size_t i;
@@ -428,7 +481,8 @@ char *ssh_get_hexa(const unsigned char *what, size_t len) {
/**
* @deprecated Please use ssh_print_hash() instead
*/
-void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
+void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len)
+{
char *hexa = ssh_get_hexa(what, len);
if (hexa == NULL) {
@@ -455,7 +509,7 @@ void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
* " 00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................"
*
* The value for each byte as corresponding ASCII character is printed at the
- * end if the value is printable. Otherwise it is replace with '.'.
+ * end if the value is printable. Otherwise, it is replaced with '.'.
*
* @param[in] descr A description for the content to be logged
* @param[in] what The buffer to be logged
@@ -622,7 +676,7 @@ void ssh_log_hexdump(const char *descr, const unsigned char *what, size_t len)
return;
error:
- SSH_LOG(SSH_LOG_WARN, "Could not print to buffer");
+ SSH_LOG(SSH_LOG_DEBUG, "Could not print to buffer");
return;
}
@@ -649,48 +703,55 @@ error:
* }
* @endcode
*/
-const char *ssh_version(int req_version) {
- if (req_version <= LIBSSH_VERSION_INT) {
- return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING MBED_STRING
- ZLIB_STRING;
- }
+const char *ssh_version(int req_version)
+{
+ if (req_version <= LIBSSH_VERSION_INT) {
+ return SSH_STRINGIFY(LIBSSH_VERSION) GCRYPT_STRING CRYPTO_STRING
+ MBED_STRING ZLIB_STRING;
+ }
- return NULL;
+ return NULL;
}
-struct ssh_list *ssh_list_new(void) {
- struct ssh_list *ret=malloc(sizeof(struct ssh_list));
- if(!ret)
- return NULL;
- ret->root=ret->end=NULL;
- return ret;
+struct ssh_list *ssh_list_new(void)
+{
+ struct ssh_list *ret = malloc(sizeof(struct ssh_list));
+ if (ret == NULL) {
+ return NULL;
+ }
+ ret->root = ret->end = NULL;
+ return ret;
}
-void ssh_list_free(struct ssh_list *list){
- struct ssh_iterator *ptr,*next;
- if(!list)
- return;
- ptr=list->root;
- while(ptr){
- next=ptr->next;
- SAFE_FREE(ptr);
- ptr=next;
- }
- SAFE_FREE(list);
+void ssh_list_free(struct ssh_list *list)
+{
+ struct ssh_iterator *ptr, *next;
+ if (!list)
+ return;
+ ptr = list->root;
+ while (ptr) {
+ next = ptr->next;
+ SAFE_FREE(ptr);
+ ptr = next;
+ }
+ SAFE_FREE(list);
}
-struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list){
- if(!list)
- return NULL;
- return list->root;
+struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
+{
+ if (!list)
+ return NULL;
+ return list->root;
}
-struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){
- struct ssh_iterator *it;
- for(it = ssh_list_get_iterator(list); it != NULL ;it=it->next)
- if(it->data==value)
- return it;
- return NULL;
+struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value)
+{
+ struct ssh_iterator *it;
+
+ for (it = ssh_list_get_iterator(list); it != NULL ; it = it->next)
+ if (it->data == value)
+ return it;
+ return NULL;
}
/**
@@ -703,7 +764,7 @@ struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value){
size_t ssh_list_count(const struct ssh_list *list)
{
struct ssh_iterator *it = NULL;
- int count = 0;
+ size_t count = 0;
for (it = ssh_list_get_iterator(list); it != NULL ; it = it->next) {
count++;
@@ -712,16 +773,20 @@ size_t ssh_list_count(const struct ssh_list *list)
return count;
}
-static struct ssh_iterator *ssh_iterator_new(const void *data){
- struct ssh_iterator *iterator=malloc(sizeof(struct ssh_iterator));
- if(!iterator)
- return NULL;
- iterator->next=NULL;
- iterator->data=data;
- return iterator;
+static struct ssh_iterator *ssh_iterator_new(const void *data)
+{
+ struct ssh_iterator *iterator = malloc(sizeof(struct ssh_iterator));
+
+ if (iterator == NULL) {
+ return NULL;
+ }
+ iterator->next = NULL;
+ iterator->data = data;
+ return iterator;
}
-int ssh_list_append(struct ssh_list *list,const void *data){
+int ssh_list_append(struct ssh_list *list,const void *data)
+{
struct ssh_iterator *iterator = NULL;
if (list == NULL) {
@@ -744,7 +809,8 @@ int ssh_list_append(struct ssh_list *list,const void *data){
return SSH_OK;
}
-int ssh_list_prepend(struct ssh_list *list, const void *data){
+int ssh_list_prepend(struct ssh_list *list, const void *data)
+{
struct ssh_iterator *it = NULL;
if (list == NULL) {
@@ -768,8 +834,9 @@ int ssh_list_prepend(struct ssh_list *list, const void *data){
return SSH_OK;
}
-void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){
- struct ssh_iterator *ptr,*prev;
+void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator)
+{
+ struct ssh_iterator *ptr, *prev;
if (list == NULL) {
return;
@@ -803,12 +870,13 @@ void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator){
* @brief Removes the top element of the list and returns the data value
* attached to it.
*
- * @param[in[ list The ssh_list to remove the element.
+ * @param[in] list The ssh_list to remove the element.
*
* @returns A pointer to the element being stored in head, or NULL
* if the list is empty.
*/
-const void *_ssh_list_pop_head(struct ssh_list *list){
+const void *_ssh_list_pop_head(struct ssh_list *list)
+{
struct ssh_iterator *iterator = NULL;
const void *data = NULL;
@@ -834,17 +902,21 @@ const void *_ssh_list_pop_head(struct ssh_list *list){
* dirname breaks a null-terminated pathname string into a directory component.
* In the usual case, ssh_dirname() returns the string up to, but not including,
* the final '/'. Trailing '/' characters are not counted as part of the
- * pathname. The caller must free the memory.
+ * pathname. The caller must free the memory using ssh_string_free_char().
*
* @param[in] path The path to parse.
*
* @return The dirname of path or NULL if we can't allocate memory.
* If path does not contain a slash, c_dirname() returns
- * the string ".". If path is the string "/", it returns
+ * the string ".". If path is a string "/", it returns
* the string "/". If path is NULL or an empty string,
- * "." is returned.
+ * "." is returned. The memory needs to be freed using
+ * ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_dirname (const char *path) {
+char *ssh_dirname (const char *path)
+{
char *new = NULL;
size_t len;
@@ -895,11 +967,15 @@ char *ssh_dirname (const char *path) {
* @param[in] path The path to parse.
*
* @return The filename of path or NULL if we can't allocate
- * memory. If path is a the string "/", basename returns
+ * memory. If path is the string "/", basename returns
* the string "/". If path is NULL or an empty string,
- * "." is returned.
+ * "." is returned. The caller needs to free this memory
+ * ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_basename (const char *path) {
+char *ssh_basename (const char *path)
+{
char *new = NULL;
const char *s;
size_t len;
@@ -1032,9 +1108,13 @@ int ssh_mkdirs(const char *pathname, mode_t mode)
*
* @param[in] d The directory to expand.
*
- * @return The expanded directory, NULL on error.
+ * @return The expanded directory, NULL on error. The caller
+ * needs to free the memory using ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_path_expand_tilde(const char *d) {
+char *ssh_path_expand_tilde(const char *d)
+{
char *h = NULL, *r;
const char *p;
size_t ld;
@@ -1101,12 +1181,17 @@ char *ssh_path_expand_tilde(const char *d) {
* %l local hostname
* %r remote username
* %p remote port
- * @returns Expanded string.
+ * @returns Expanded string. The caller needs to free the memory using
+ * ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
-char *ssh_path_expand_escape(ssh_session session, const char *s) {
- char host[NI_MAXHOST];
- char buf[MAX_BUF_SIZE];
- char *r, *x = NULL;
+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;
const char *p;
size_t i, l;
@@ -1122,6 +1207,13 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
return NULL;
}
+ buf = malloc(MAX_BUF_SIZE);
+ if (buf == NULL) {
+ ssh_set_error_oom(session);
+ free(r);
+ return NULL;
+ }
+
p = r;
buf[0] = '\0';
@@ -1131,6 +1223,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
buf[i] = *p;
i++;
if (i >= MAX_BUF_SIZE) {
+ free(buf);
free(r);
return NULL;
}
@@ -1147,7 +1240,15 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
case '%':
goto escape;
case 'd':
- x = strdup(session->opts.sshdir);
+ if (session->opts.sshdir) {
+ x = strdup(session->opts.sshdir);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand sshdir");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'u':
x = ssh_get_local_username();
@@ -1158,31 +1259,48 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
}
break;
case 'h':
- x = strdup(session->opts.host);
+ if (session->opts.host) {
+ x = strdup(session->opts.host);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand host");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'r':
- x = strdup(session->opts.username);
+ if (session->opts.username) {
+ x = strdup(session->opts.username);
+ } else {
+ ssh_set_error(session, SSH_FATAL,
+ "Cannot expand username");
+ free(buf);
+ free(r);
+ return NULL;
+ }
break;
case 'p':
- if (session->opts.port < 65536) {
- char tmp[6];
-
- snprintf(tmp,
- sizeof(tmp),
- "%u",
- session->opts.port > 0 ? session->opts.port : 22);
- x = strdup(tmp);
+ {
+ char tmp[6];
+
+ snprintf(tmp, sizeof(tmp), "%hu",
+ (uint16_t)(session->opts.port > 0 ? session->opts.port
+ : 22));
+ x = strdup(tmp);
}
break;
default:
ssh_set_error(session, SSH_FATAL,
"Wrong escape sequence detected");
+ free(buf);
free(r);
return NULL;
}
if (x == NULL) {
ssh_set_error_oom(session);
+ free(buf);
free(r);
return NULL;
}
@@ -1191,19 +1309,26 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) {
if (i >= MAX_BUF_SIZE) {
ssh_set_error(session, SSH_FATAL,
"String too long");
+ free(buf);
free(x);
free(r);
return NULL;
}
l = strlen(buf);
- strncpy(buf + l, x, sizeof(buf) - l - 1);
+ strncpy(buf + l, x, MAX_BUF_SIZE - l - 1);
buf[i] = '\0';
SAFE_FREE(x);
}
free(r);
- return strdup(buf);
-#undef MAX_BUF_SIZE
+
+ /* strip the unused space by realloc */
+ x = realloc(buf, strlen(buf) + 1);
+ if (x == NULL) {
+ ssh_set_error_oom(session);
+ free(buf);
+ }
+ return x;
}
/**
@@ -1249,7 +1374,7 @@ int ssh_analyze_banner(ssh_session session, int server)
return -1;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Analyzing banner: %s", banner);
+ SSH_LOG(SSH_LOG_DEBUG, "Analyzing banner: %s", banner);
switch (banner[4]) {
case '2':
@@ -1279,27 +1404,31 @@ int ssh_analyze_banner(ssh_session session, int server)
* 012345678901234567890
*/
if (strlen(openssh) > 9) {
+ errno = 0;
major = strtoul(openssh + 8, &tmp, 10);
if ((tmp == (openssh + 8)) ||
((errno == ERANGE) && (major == ULONG_MAX)) ||
((errno != 0) && (major == 0)) ||
((major < 1) || (major > 100))) {
/* invalid major */
+ errno = 0;
goto done;
}
+ errno = 0;
minor = strtoul(openssh + 10, &tmp, 10);
if ((tmp == (openssh + 10)) ||
((errno == ERANGE) && (major == ULONG_MAX)) ||
((errno != 0) && (major == 0)) ||
(minor > 100)) {
/* invalid minor */
+ errno = 0;
goto done;
}
session->openssh = SSH_VERSION_INT(((int) major), ((int) minor), 0);
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"We are talking to an OpenSSH %s version: %lu.%lu (%x)",
server ? "client" : "server",
major, minor, session->openssh);
@@ -1322,7 +1451,8 @@ done:
* @brief initializes a timestamp to the current time
* @param[out] ts pointer to an allocated ssh_timestamp structure
*/
-void ssh_timestamp_init(struct ssh_timestamp *ts){
+void ssh_timestamp_init(struct ssh_timestamp *ts)
+{
#ifdef HAVE_CLOCK_GETTIME
struct timespec tp;
clock_gettime(CLOCK, &tp);
@@ -1345,17 +1475,18 @@ void ssh_timestamp_init(struct ssh_timestamp *ts){
* @returns difference in milliseconds
*/
-static int ssh_timestamp_difference(struct ssh_timestamp *old,
- struct ssh_timestamp *new){
- long seconds, usecs, msecs;
- seconds = new->seconds - old->seconds;
- usecs = new->useconds - old->useconds;
- if (usecs < 0){
- seconds--;
- usecs += 1000000;
- }
- msecs = seconds * 1000 + usecs/1000;
- return msecs;
+static int
+ssh_timestamp_difference(struct ssh_timestamp *old, struct ssh_timestamp *new)
+{
+ long seconds, usecs, msecs;
+ seconds = new->seconds - old->seconds;
+ usecs = new->useconds - old->useconds;
+ if (usecs < 0){
+ seconds--;
+ usecs += 1000000;
+ }
+ msecs = seconds * 1000 + usecs/1000;
+ return msecs;
}
/**
@@ -1366,14 +1497,20 @@ static int ssh_timestamp_difference(struct ssh_timestamp *old,
* @param[in] usec number of microseconds
* @returns milliseconds, or 10000 if user supplied values are equal to zero
*/
-int ssh_make_milliseconds(long sec, long usec) {
- int res = usec ? (usec / 1000) : 0;
+int ssh_make_milliseconds(unsigned long sec, unsigned long usec)
+{
+ unsigned long res = usec ? (usec / 1000) : 0;
res += (sec * 1000);
if (res == 0) {
res = 10 * 1000; /* use a reasonable default value in case
* SSH_OPTIONS_TIMEOUT is not set in options. */
}
- return res;
+
+ if (res > INT_MAX) {
+ return SSH_TIMEOUT_INFINITE;
+ } else {
+ return (int)res;
+ }
}
/**
@@ -1386,7 +1523,8 @@ int ssh_make_milliseconds(long sec, long usec) {
* @returns 1 if timeout is elapsed
* 0 otherwise
*/
-int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
+int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout)
+{
struct ssh_timestamp now;
switch(timeout) {
@@ -1394,7 +1532,7 @@ int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
* -2 means user-defined timeout as available in
* session->timeout, session->timeout_usec.
*/
- SSH_LOG(SSH_LOG_WARN, "ssh_timeout_elapsed called with -2. this needs to "
+ SSH_LOG(SSH_LOG_DEBUG, "ssh_timeout_elapsed called with -2. this needs to "
"be fixed. please set a breakpoint on misc.c:%d and "
"fix the caller\n", __LINE__);
return 0;
@@ -1418,7 +1556,8 @@ int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout) {
* timeout
* @returns remaining time in milliseconds, 0 if elapsed, -1 if never.
*/
-int ssh_timeout_update(struct ssh_timestamp *ts, int timeout){
+int ssh_timeout_update(struct ssh_timestamp *ts, int timeout)
+{
struct ssh_timestamp now;
int ms, ret;
if (timeout <= 0) {
@@ -1547,20 +1686,20 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len)
enum ssh_quote_state_e state = NO_QUOTE;
if (file_name == NULL || buf == NULL || buf_len == 0) {
- SSH_LOG(SSH_LOG_WARNING, "Invalid parameter");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid parameter");
return SSH_ERROR;
}
/* Only allow file names smaller than 32kb. */
if (strlen(file_name) > 32 * 1024) {
- SSH_LOG(SSH_LOG_WARNING, "File name too long");
+ SSH_LOG(SSH_LOG_TRACE, "File name too long");
return SSH_ERROR;
}
/* Paranoia check */
required_buf_len = (size_t)3 * strlen(file_name) + 1;
if (required_buf_len > buf_len) {
- SSH_LOG(SSH_LOG_WARNING, "Buffer too small");
+ SSH_LOG(SSH_LOG_TRACE, "Buffer too small");
return SSH_ERROR;
}
@@ -1611,13 +1750,13 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len)
*dst++ = '\\';
break;
case SINGLE_QUOTE:
- /* Close the current quoted string and replace '!' for unquoted
+ /* Close the currently quoted string and replace '!' for unquoted
* "\!" */
*dst++ = '\'';
*dst++ = '\\';
break;
case DOUBLE_QUOTE:
- /* Close current quoted string and replace "!" for unquoted
+ /* Close currently quoted string and replace "!" for unquoted
* "\!" */
*dst++ = '"';
*dst++ = '\\';
@@ -1718,7 +1857,7 @@ int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
}
if ((2 * strlen(string) + 1) > buf_len) {
- SSH_LOG(SSH_LOG_WARNING, "Buffer too small");
+ SSH_LOG(SSH_LOG_TRACE, "Buffer too small");
return SSH_ERROR;
}
@@ -1741,21 +1880,23 @@ int ssh_newline_vis(const char *string, char *buf, size_t buf_len)
*
* @brief Replaces the last 6 characters of a string from 'X' to 6 random hexdigits.
*
- * @param[in] template Any input string with last 6 characters as 'X'.
+ * @param[in,out] name Any input string with last 6 characters as 'X'.
* @returns -1 as error when the last 6 characters of the input to be replaced are not 'X'
* 0 otherwise.
*/
-int ssh_tmpname(char *template)
+int ssh_tmpname(char *name)
{
char *tmp = NULL;
size_t i = 0;
+ int rc = 0;
+ uint8_t random[6];
- if (template == NULL) {
+ if (name == NULL) {
goto err;
}
- tmp = template + strlen(template) - 6;
- if (tmp < template) {
+ tmp = name + strlen(name) - 6;
+ if (tmp < name) {
goto err;
}
@@ -1767,17 +1908,18 @@ int ssh_tmpname(char *template)
}
}
- srand(time(NULL));
+ rc = ssh_get_random(random, 6, 0);
+ if (!rc) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Could not generate random data\n");
+ goto err;
+ }
- for (i = 0; i < 6; ++i) {
-#ifdef _WIN32
- /* in win32 MAX_RAND is 32767, thus we can not shift that far,
- * otherwise the last three chars are 0 */
- int hexdigit = (rand() >> (i * 2)) & 0x1f;
-#else
- int hexdigit = (rand() >> (i * 5)) & 0x1f;
-#endif
- tmp[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
+ for (i = 0; i < 6; i++) {
+ /* Limit the random[i] < 32 */
+ random[i] &= 0x1f;
+ /* For values from 0 to 9 use numbers, otherwise use letters */
+ tmp[i] = random[i] > 9 ? random[i] + 'a' - 10 : random[i] + '0';
}
return 0;
@@ -1790,15 +1932,19 @@ err:
/**
* @internal
*
- * @brief Finds the first occurence of a patterm in a string and replaces it.
+ * @brief Finds the first occurrence of a pattern in a string and replaces it.
*
- * @param[in] src Source string containing the patern to be replaced.
+ * @param[in] src Source string containing the pattern to be replaced.
* @param[in] pattern Pattern to be replaced in the source string.
- * Note: this function replaces the first occurence of pattern only.
+ * Note: this function replaces the first occurrence of
+ * pattern only.
* @param[in] replace String to be replaced is stored in replace.
*
* @returns src_replaced a pointer that points to the replaced string.
- * NULL if allocation fails or if src is NULL.
+ * NULL if allocation fails or if src is NULL. The returned memory needs to be
+ * freed using ssh_string_free_char().
+ *
+ * @see ssh_string_free_char()
*/
char *ssh_strreplace(const char *src, const char *pattern, const char *replace)
{
@@ -1838,4 +1984,247 @@ char *ssh_strreplace(const char *src, const char *pattern, const char *replace)
}
}
+/**
+ * @internal
+ *
+ * @brief Processes errno into error string
+ *
+ * @param[in] err_num The errno value
+ * @param[out] buf Pointer to a place where the string could be saved
+ * @param[in] buflen The allocated size of buf
+ *
+ * @return error string
+ */
+char *ssh_strerror(int err_num, char *buf, size_t buflen)
+{
+#if defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE)
+ /* GNU extension on Linux */
+ return strerror_r(err_num, buf, buflen);
+#else
+ int rv;
+
+#if defined(_WIN32)
+ rv = strerror_s(buf, buflen, err_num);
+#else
+ /* POSIX version available for example on FreeBSD or in musl libc */
+ rv = strerror_r(err_num, buf, buflen);
+#endif /* _WIN32 */
+
+ /* make sure the buffer is initialized and terminated with NULL */
+ if (-rv == ERANGE) {
+ buf[0] = '\0';
+ }
+ return buf;
+#endif /* defined(__linux__) && defined(__GLIBC__) && defined(_GNU_SOURCE) */
+}
+
+/**
+ * @brief Read the requested number of bytes from a local file.
+ *
+ * A call to read() may perform a short read even when sufficient data is
+ * present in the file. This function can be used to avoid such short reads.
+ *
+ * This function tries to read the requested number of bytes from the file
+ * until one of the following occurs :
+ * - Requested number of bytes are read.
+ * - EOF is encountered before reading the requested number of bytes.
+ * - An error occurs.
+ *
+ * On encountering an error due to an interrupt, this function ignores that
+ * error and continues trying to read the data.
+ *
+ * @param[in] fd The file descriptor of the local file to read from.
+ *
+ * @param[out] buf Pointer to a buffer in which read data will be
+ * stored.
+ *
+ * @param[in] nbytes Number of bytes to read.
+ *
+ * @returns Number of bytes read on success,
+ * SSH_ERROR on error with errno set to indicate the
+ * error.
+ */
+ssize_t ssh_readn(int fd, void *buf, size_t nbytes)
+{
+ size_t total_bytes_read = 0;
+ ssize_t bytes_read;
+
+ if (fd < 0 || buf == NULL || nbytes == 0) {
+ errno = EINVAL;
+ return SSH_ERROR;
+ }
+
+ do {
+ bytes_read = read(fd,
+ ((char *)buf) + total_bytes_read,
+ nbytes - total_bytes_read);
+ if (bytes_read == -1) {
+ if (errno == EINTR) {
+ /* Ignoring errors due to signal interrupts */
+ continue;
+ }
+
+ return SSH_ERROR;
+ }
+
+ if (bytes_read == 0) {
+ /* EOF encountered on the local file before reading nbytes */
+ break;
+ }
+
+ total_bytes_read += (size_t)bytes_read;
+ } while (total_bytes_read < nbytes);
+
+ return total_bytes_read;
+}
+
+/**
+ * @brief Write the requested number of bytes to a local file.
+ *
+ * A call to write() may perform a short write on a local file. This function
+ * can be used to avoid short writes.
+ *
+ * This function tries to write the requested number of bytes until those many
+ * bytes are written or some error occurs.
+ *
+ * On encountering an error due to an interrupt, this function ignores that
+ * error and continues trying to write the data.
+ *
+ * @param[in] fd The file descriptor of the local file to write to.
+ *
+ * @param[in] buf Pointer to a buffer in which data to write is stored.
+ *
+ * @param[in] nbytes Number of bytes to write.
+ *
+ * @returns Number of bytes written on success,
+ * SSH_ERROR on error with errno set to indicate the
+ * error.
+ */
+ssize_t ssh_writen(int fd, const void *buf, size_t nbytes)
+{
+ size_t total_bytes_written = 0;
+ ssize_t bytes_written;
+
+ if (fd < 0 || buf == NULL || nbytes == 0) {
+ errno = EINVAL;
+ return SSH_ERROR;
+ }
+
+ do {
+ bytes_written = write(fd,
+ ((const char *)buf) + total_bytes_written,
+ nbytes - total_bytes_written);
+ if (bytes_written == -1) {
+ if(errno == EINTR) {
+ /* Ignoring errors due to signal interrupts */
+ continue;
+ }
+
+ return SSH_ERROR;
+ }
+
+ total_bytes_written += (size_t)bytes_written;
+ } while (total_bytes_written < nbytes);
+
+ return total_bytes_written;
+}
+
+/**
+ * @brief Checks syntax of a domain name
+ *
+ * The check is made based on the RFC1035 section 2.3.1
+ * Allowed characters are: hyphen, period, digits (0-9) and letters (a-zA-Z)
+ *
+ * The label should be no longer than 63 characters
+ * The label should start with a letter and end with a letter or number
+ * The label in this implementation can start with a number to allow virtual
+ * URLs to pass. Note that this will make IPv4 addresses to pass
+ * this check too.
+ *
+ * @param hostname The domain name to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the hostname passes syntax check
+ * SSH_ERROR otherwise or if hostname is NULL or empty string
+ */
+int ssh_check_hostname_syntax(const char *hostname)
+{
+ char *it = NULL, *s = NULL, *buf = NULL;
+ size_t it_len;
+ char c;
+
+ if (hostname == NULL || strlen(hostname) == 0) {
+ return SSH_ERROR;
+ }
+
+ /* strtok_r writes into the string, keep the input clean */
+ s = strdup(hostname);
+ if (s == NULL) {
+ return SSH_ERROR;
+ }
+
+ it = strtok_r(s, ".", &buf);
+ /* if the token has 0 length */
+ if (it == NULL) {
+ free(s);
+ return SSH_ERROR;
+ }
+ do {
+ it_len = strlen(it);
+ if (it_len > ARPA_DOMAIN_MAX_LEN ||
+ /* the first char must be a letter, but some virtual urls start
+ * with a number */
+ isalnum(it[0]) == 0 ||
+ isalnum(it[it_len - 1]) == 0) {
+ free(s);
+ return SSH_ERROR;
+ }
+ while (*it != '\0') {
+ c = *it;
+ /* the "." is allowed too, but tokenization removes it from the
+ * string */
+ if (isalnum(c) == 0 && c != '-') {
+ free(s);
+ return SSH_ERROR;
+ }
+ it++;
+ }
+ } while ((it = strtok_r(NULL, ".", &buf)) != NULL);
+
+ free(s);
+
+ return SSH_OK;
+}
+
+/**
+ * @brief Checks syntax of a username
+ *
+ * This check disallows metacharacters in the username
+ *
+ * @param username The username to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the username passes syntax check
+ * SSH_ERROR otherwise or if username is NULL or empty string
+ */
+int ssh_check_username_syntax(const char *username)
+{
+ size_t username_len;
+
+ if (username == NULL || *username == '-') {
+ return SSH_ERROR;
+ }
+
+ username_len = strlen(username);
+ if (username_len == 0 || username[username_len - 1] == '\\' ||
+ strpbrk(username, "'`\";&<>|(){}") != NULL) {
+ return SSH_ERROR;
+ }
+ for (size_t i = 0; i < username_len; i++) {
+ if (isspace(username[i]) != 0 && username[i + 1] == '-') {
+ return SSH_ERROR;
+ }
+ }
+
+ return SSH_OK;
+}
+
/** @} */
diff --git a/src/options.c b/src/options.c
index 692fc837..61a7e4a7 100644
--- a/src/options.c
+++ b/src/options.c
@@ -32,10 +32,12 @@
#include <winsock2.h>
#endif
#include <sys/types.h>
+#include "libssh/pki_priv.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/options.h"
+#include "libssh/config_parser.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/bind.h"
@@ -51,21 +53,23 @@
* @brief Duplicate the options of a session structure.
*
* If you make several sessions with the same options this is useful. You
- * cannot use twice the same option structure in ssh_session_connect.
+ * cannot use twice the same option structure in ssh_connect.
*
* @param src The session to use to copy the options.
*
* @param dest A pointer to store the allocated session with duplicated
- * options. You have to free the memory.
+ * options. You have to free the memory using ssh_free()
*
- * @returns 0 on sucess, -1 on error with errno set.
+ * @returns 0 on success, -1 on error with errno set.
*
- * @see ssh_session_connect()
+ * @see ssh_connect()
+ * @see ssh_free()
*/
int ssh_options_copy(ssh_session src, ssh_session *dest)
{
ssh_session new;
struct ssh_iterator *it = NULL;
+ struct ssh_list *list = NULL;
char *id = NULL;
int i;
@@ -103,24 +107,51 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
/* Remove the default identities */
- for (id = ssh_list_pop_head(char *, new->opts.identity);
+ for (id = ssh_list_pop_head(char *, new->opts.identity_non_exp);
id != NULL;
- id = ssh_list_pop_head(char *, new->opts.identity)) {
+ id = ssh_list_pop_head(char *, new->opts.identity_non_exp)) {
SAFE_FREE(id);
}
/* Copy the new identities from the source list */
- if (src->opts.identity != NULL) {
+ list = new->opts.identity_non_exp;
+ it = ssh_list_get_iterator(src->opts.identity_non_exp);
+ for (i = 0; i < 2; i++) {
+ while (it) {
+ int rc;
+
+ id = strdup((char *)it->data);
+ if (id == NULL) {
+ ssh_free(new);
+ return -1;
+ }
+
+ rc = ssh_list_append(list, id);
+ if (rc < 0) {
+ free(id);
+ ssh_free(new);
+ return -1;
+ }
+ it = it->next;
+ }
+
+ /* copy the identity list if there is any already */
+ list = new->opts.identity;
it = ssh_list_get_iterator(src->opts.identity);
+ }
+
+ list = new->opts.certificate_non_exp;
+ it = ssh_list_get_iterator(src->opts.certificate_non_exp);
+ for (i = 0; i < 2; i++) {
while (it) {
int rc;
- id = strdup((char *) it->data);
+ id = strdup((char *)it->data);
if (id == NULL) {
ssh_free(new);
return -1;
}
- rc = ssh_list_append(new->opts.identity, id);
+ rc = ssh_list_append(list, id);
if (rc < 0) {
free(id);
ssh_free(new);
@@ -128,6 +159,10 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
it = it->next;
}
+
+ /* copy the certificate list if there is any already */
+ list = new->opts.certificate;
+ it = ssh_list_get_iterator(src->opts.certificate);
}
if (src->opts.sshdir != NULL) {
@@ -196,6 +231,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
}
+ if (src->opts.control_path != NULL) {
+ new->opts.control_path = strdup(src->opts.control_path);
+ if (new->opts.control_path == NULL) {
+ ssh_free(new);
+ return -1;
+ }
+ }
+
memcpy(new->opts.options_seen, src->opts.options_seen,
sizeof(new->opts.options_seen));
@@ -209,6 +252,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
new->opts.flags = src->opts.flags;
new->opts.nodelay = src->opts.nodelay;
new->opts.config_processed = src->opts.config_processed;
+ new->opts.control_master = src->opts.control_master;
new->common.log_verbosity = src->common.log_verbosity;
new->common.callbacks = src->common.callbacks;
@@ -219,14 +263,30 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
- const char *list)
+ const char *list,
+ char **place)
{
- char *p = NULL;
+ /* When the list start with +,-,^ the filtration of unknown algorithms
+ * gets handled inside the helper functions, otherwise the list is taken
+ * as it is. */
+ char *p = (char *)list;
+
+ if (algo < SSH_COMP_C_S) {
+ if (list[0] == '+') {
+ p = ssh_add_to_default_algos(algo, list+1);
+ } else if (list[0] == '-') {
+ p = ssh_remove_from_default_algos(algo, list+1);
+ } else if (list[0] == '^') {
+ p = ssh_prefix_default_algos(algo, list+1);
+ }
+ }
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(algo, list);
- } else {
- p = ssh_keep_known_algos(algo, list);
+ if (p == list) {
+ if (ssh_fips_mode()) {
+ p = ssh_keep_fips_algos(algo, list);
+ } else {
+ p = ssh_keep_known_algos(algo, list);
+ }
}
if (p == NULL) {
@@ -236,8 +296,8 @@ int ssh_options_set_algo(ssh_session session,
return -1;
}
- SAFE_FREE(session->opts.wanted_methods[algo]);
- session->opts.wanted_methods[algo] = p;
+ SAFE_FREE(*place);
+ *place = p;
return 0;
}
@@ -263,9 +323,10 @@ int ssh_options_set_algo(ssh_session session,
* The file descriptor to use (socket_t).\n
* \n
* If you wish to open the socket yourself for a reason
- * or another, set the file descriptor. Don't forget to
- * set the hostname as the hostname is used as a key in
- * the known_host mechanism.
+ * or another, set the file descriptor and take care of closing
+ * it (this is new behavior in libssh 0.10).
+ * Don't forget to set the hostname as the hostname is used
+ * as a key in the known_host mechanism.
*
* - SSH_OPTIONS_BINDADDR:
* The address to bind the client to (const char *).
@@ -312,13 +373,28 @@ int ssh_options_set_algo(ssh_session session,
* Add a new identity file (const char *, format string) to
* the identity list.\n
* \n
- * By default identity, id_dsa and id_rsa are checked.\n
+ * By default id_rsa, id_ecdsa and id_ed25519 files are used.\n
* \n
* The identity used to authenticate with public key will be
* prepended to the list.
* It may include "%s" which will be replaced by the
* user home directory.
*
+ * - SSH_OPTIONS_CERTIFICATE:
+ * Add a new certificate file (const char *, format string) to
+ * the certificate list.\n
+ * \n
+ * By default id_rsa-cert.pub, id_ecdsa-cert.pub and
+ * id_ed25519-cert.pub files are used, when the underlying
+ * private key is present.\n
+ * \n
+ * The certificate itself can not be used to authenticate to
+ * remote server so it needs to be paired with private key
+ * (aka identity file) provided with separate option, from agent
+ * or from PKCS#11 token.
+ * It may include "%s" which will be replaced by the
+ * user home directory.
+ *
* - SSH_OPTIONS_TIMEOUT:
* Set a timeout for the connection in seconds (long).
*
@@ -340,8 +416,9 @@ int ssh_options_set_algo(ssh_session session,
* - SSH_LOG_NOLOG: No logging
* - SSH_LOG_WARNING: Only warnings
* - SSH_LOG_PROTOCOL: High level protocol information
- * - SSH_LOG_PACKET: Lower level protocol infomations, packet level
+ * - SSH_LOG_PACKET: Lower level protocol information, packet level
* - SSH_LOG_FUNCTIONS: Every function path
+ * The default is SSH_LOG_NOLOG.
*
* - SSH_OPTIONS_LOG_VERBOSITY_STR:
* Set the session logging verbosity via a
@@ -353,34 +430,60 @@ int ssh_options_set_algo(ssh_session session,
*
* - SSH_OPTIONS_CIPHERS_C_S:
* Set the symmetric cipher client to server (const char *,
- * comma-separated list).
+ * comma-separated list). The list can be prepended by +,-,^
+ * which can append, remove or move to the beginning
+ * (prioritizing) of the default list respectively. Giving an
+ * empty list after + and ^ will cause error.
*
* - SSH_OPTIONS_CIPHERS_S_C:
* Set the symmetric cipher server to client (const char *,
- * comma-separated list).
+ * comma-separated list). The list can be prepended by +,-,^
+ * which can append, remove or move to the beginning
+ * (prioritizing) of the default list respectively. Giving an
+ * empty list after + and ^ will cause error.
*
* - SSH_OPTIONS_KEY_EXCHANGE:
* Set the key exchange method to be used (const char *,
* comma-separated list). ex:
* "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+ * The list can be prepended by +,-,^ which will append,
+ * remove or move to the beginning (prioritizing) of the
+ * default list respectively. Giving an empty list
+ * after + and ^ will cause error.
*
* - SSH_OPTIONS_HMAC_C_S:
* Set the Message Authentication Code algorithm client to server
- * (const char *, comma-separated list).
+ * (const char *, comma-separated list). The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_HMAC_S_C:
* Set the Message Authentication Code algorithm server to client
- * (const char *, comma-separated list).
+ * (const char *, comma-separated list). The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_HOSTKEYS:
* Set the preferred server host key types (const char *,
* comma-separated list). ex:
- * "ssh-rsa,ssh-dss,ecdh-sha2-nistp256"
+ * "ssh-rsa,ecdh-sha2-nistp256". The list can be
+ * prepended by +,-,^ which will append, remove or move to
+ * the beginning (prioritizing) of the default list
+ * respectively. Giving an empty list after + and ^ will
+ * cause error.
*
* - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
* Set the preferred public key algorithms to be used for
* authentication (const char *, comma-separated list). ex:
- * "ssh-rsa,rsa-sha2-256,ssh-dss,ecdh-sha2-nistp256"
+ * "ssh-rsa,rsa-sha2-256,ecdh-sha2-nistp256"
+ * The list can be prepended by +,-,^ which will append,
+ * remove or move to the beginning (prioritizing) of the
+ * default list respectively. Giving an empty list
+ * after + and ^ will cause error.
*
* - SSH_OPTIONS_COMPRESSION_C_S:
* Set the compression to use for client to server
@@ -461,10 +564,49 @@ int ssh_options_set_algo(ssh_session session,
* (uint64_t, 0=default)
*
* - SSH_OPTIONS_REKEY_TIME
- * Set the time limit for a session before intializing a rekey
+ * Set the time limit for a session before initializing a rekey
* in seconds. RFC 4253 Section 9 recommends one hour.
* (uint32_t, 0=off)
*
+ * - SSH_OPTIONS_RSA_MIN_SIZE
+ * Set the minimum RSA key size in bits to be accepted by the
+ * client for both authentication and hostkey verification.
+ * The values under 768 bits are not accepted even with this
+ * configuration option as they are considered completely broken.
+ * Setting 0 will revert the value to defaults.
+ * Default is 1024 bits or 2048 bits in FIPS mode.
+ * (int *)
+
+ * - SSH_OPTIONS_IDENTITY_AGENT
+ * Set the path to the SSH agent socket. If unset, the
+ * SSH_AUTH_SOCK environment is consulted.
+ * (const char *)
+
+ * - SSH_OPTIONS_IDENTITIES_ONLY
+ * Use only keys specified in the SSH config, even if agent
+ * offers more.
+ * (bool)
+ *
+ * - SSH_OPTIONS_CONTROL_MASTER
+ * Set the option to enable the sharing of multiple sessions over a
+ * single network connection using connection multiplexing.
+ *
+ * The possible options are among the following:
+ * - SSH_CONTROL_MASTER_AUTO: enable connection sharing if possible
+ * - SSH_CONTROL_MASTER_YES: enable connection sharing unconditionally
+ * - SSH_CONTROL_MASTER_ASK: ask for confirmation if connection sharing is to be enabled
+ * - SSH_CONTROL_MASTER_AUTOASK: enable connection sharing if possible,
+ * but ask for confirmation
+ * - SSH_CONTROL_MASTER_NO: disable connection sharing unconditionally
+ *
+ * The default is SSH_CONTROL_MASTER_NO.
+ *
+ * - SSH_OPTIONS_CONTROL_PATH
+ * Set the path to the control socket used for connection sharing.
+ * Set to "none" to disable connection sharing.
+ * (const char *)
+ *
+ *
* @param value The value to set. This is a generic pointer and the
* datatype which is used should be set according to the
* type set.
@@ -472,12 +614,14 @@ int ssh_options_set_algo(ssh_session session,
* @return 0 on success, < 0 on error.
*/
int ssh_options_set(ssh_session session, enum ssh_options_e type,
- const void *value) {
+ const void *value)
+{
const char *v;
char *p, *q;
long int i;
unsigned int u;
int rc;
+ char **wanted_methods = session->opts.wanted_methods;
if (session == NULL) {
return -1;
@@ -490,33 +634,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- q = strdup(value);
- if (q == NULL) {
- ssh_set_error_oom(session);
+ char *username = NULL, *hostname = NULL;
+ rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
+ if (rc != SSH_OK) {
return -1;
}
- p = strchr(q, '@');
-
- SAFE_FREE(session->opts.host);
-
- if (p) {
- *p = '\0';
- session->opts.host = strdup(p + 1);
- if (session->opts.host == NULL) {
- SAFE_FREE(q);
- ssh_set_error_oom(session);
- return -1;
- }
-
+ if (username != NULL) {
SAFE_FREE(session->opts.username);
- session->opts.username = strdup(q);
- SAFE_FREE(q);
- if (session->opts.username == NULL) {
- ssh_set_error_oom(session);
- return -1;
- }
- } else {
- session->opts.host = q;
+ session->opts.username = username;
+ }
+ if (hostname != NULL) {
+ SAFE_FREE(session->opts.host);
+ session->opts.host = hostname;
}
}
break;
@@ -547,7 +676,9 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
}
i = strtol(q, &p, 10);
if (q == p) {
+ SSH_LOG(SSH_LOG_DEBUG, "No port number was parsed");
SAFE_FREE(q);
+ return -1;
}
SAFE_FREE(q);
if (i <= 0) {
@@ -607,6 +738,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ rc = ssh_check_username_syntax(session->opts.username);
+ if (rc != SSH_OK) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
}
break;
case SSH_OPTIONS_SSH_DIR:
@@ -639,7 +775,27 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
if (q == NULL) {
return -1;
}
- rc = ssh_list_prepend(session->opts.identity, q);
+ if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
+ rc = ssh_list_append(session->opts.identity_non_exp, q);
+ } else {
+ rc = ssh_list_prepend(session->opts.identity_non_exp, q);
+ }
+ if (rc < 0) {
+ free(q);
+ return -1;
+ }
+ break;
+ case SSH_OPTIONS_CERTIFICATE:
+ v = value;
+ if (v == NULL || v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
+ q = strdup(v);
+ if (q == NULL) {
+ return -1;
+ }
+ rc = ssh_list_append(session->opts.certificate_non_exp, q);
if (rc < 0) {
free(q);
return -1;
@@ -659,6 +815,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
break;
case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
@@ -680,6 +837,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
}
break;
case SSH_OPTIONS_TIMEOUT:
@@ -743,7 +901,9 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
}
i = strtol(q, &p, 10);
if (q == p) {
+ SSH_LOG(SSH_LOG_DEBUG, "No log verbositiy was parsed");
SAFE_FREE(q);
+ return -1;
}
SAFE_FREE(q);
if (i < 0) {
@@ -761,7 +921,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_CRYPT_C_S, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_CRYPT_C_S,
+ v,
+ &wanted_methods[SSH_CRYPT_C_S]);
+ if (rc < 0)
return -1;
}
break;
@@ -771,7 +935,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_CRYPT_S_C, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_CRYPT_S_C,
+ v,
+ &wanted_methods[SSH_CRYPT_S_C]);
+ if (rc < 0)
return -1;
}
break;
@@ -781,7 +949,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_KEX, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_KEX,
+ v,
+ &wanted_methods[SSH_KEX]);
+ if (rc < 0)
return -1;
}
break;
@@ -791,7 +963,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_HOSTKEYS, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_HOSTKEYS,
+ v,
+ &wanted_methods[SSH_HOSTKEYS]);
+ if (rc < 0)
return -1;
}
break;
@@ -801,20 +977,12 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
- } else {
- p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
- }
- if (p == NULL) {
- ssh_set_error(session, SSH_REQUEST_DENIED,
- "Setting method: no known public key algorithm (%s)",
- v);
+ rc = ssh_options_set_algo(session,
+ SSH_HOSTKEYS,
+ v,
+ &session->opts.pubkey_accepted_types);
+ if (rc < 0)
return -1;
- }
-
- SAFE_FREE(session->opts.pubkey_accepted_types);
- session->opts.pubkey_accepted_types = p;
}
break;
case SSH_OPTIONS_HMAC_C_S:
@@ -823,7 +991,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_MAC_C_S, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_MAC_C_S,
+ v,
+ &wanted_methods[SSH_MAC_C_S]);
+ if (rc < 0)
return -1;
}
break;
@@ -833,7 +1005,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (ssh_options_set_algo(session, SSH_MAC_S_C, v) < 0)
+ rc = ssh_options_set_algo(session,
+ SSH_MAC_S_C,
+ v,
+ &wanted_methods[SSH_MAC_S_C]);
+ if (rc < 0)
return -1;
}
break;
@@ -843,16 +1019,18 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (strcasecmp(value,"yes")==0){
- if(ssh_options_set_algo(session,SSH_COMP_C_S,"zlib@openssh.com,zlib") < 0)
- return -1;
- } else if (strcasecmp(value,"no")==0){
- if(ssh_options_set_algo(session,SSH_COMP_C_S,"none") < 0)
- return -1;
- } else {
- if (ssh_options_set_algo(session, SSH_COMP_C_S, v) < 0)
- return -1;
+ const char *tmp = v;
+ if (strcasecmp(value, "yes") == 0){
+ tmp = "zlib@openssh.com,none";
+ } else if (strcasecmp(value, "no") == 0){
+ tmp = "none,zlib@openssh.com";
}
+ rc = ssh_options_set_algo(session,
+ SSH_COMP_C_S,
+ tmp,
+ &wanted_methods[SSH_COMP_C_S]);
+ if (rc < 0)
+ return -1;
}
break;
case SSH_OPTIONS_COMPRESSION_S_C:
@@ -861,16 +1039,19 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
- if (strcasecmp(value,"yes")==0){
- if(ssh_options_set_algo(session,SSH_COMP_S_C,"zlib@openssh.com,zlib") < 0)
- return -1;
- } else if (strcasecmp(value,"no")==0){
- if(ssh_options_set_algo(session,SSH_COMP_S_C,"none") < 0)
- return -1;
- } else {
- if (ssh_options_set_algo(session, SSH_COMP_S_C, v) < 0)
- return -1;
+ const char *tmp = v;
+ if (strcasecmp(value, "yes") == 0){
+ tmp = "zlib@openssh.com,none";
+ } else if (strcasecmp(value, "no") == 0){
+ tmp = "none,zlib@openssh.com";
}
+
+ rc = ssh_options_set_algo(session,
+ SSH_COMP_S_C,
+ tmp,
+ &wanted_methods[SSH_COMP_S_C]);
+ if (rc < 0)
+ return -1;
}
break;
case SSH_OPTIONS_COMPRESSION:
@@ -906,7 +1087,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.StrictHostKeyChecking = (*x & 0xff) > 0 ? 1 : 0;
}
- session->opts.StrictHostKeyChecking = *(int*)value;
break;
case SSH_OPTIONS_PROXYCOMMAND:
v = value;
@@ -923,6 +1103,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
return -1;
}
session->opts.ProxyCommand = q;
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_PROXYCOMMAND;
}
}
break;
@@ -1029,6 +1210,77 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.rekey_time = (*x) * 1000;
}
break;
+ case SSH_OPTIONS_RSA_MIN_SIZE:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ int *x = (int *)value;
+ if (*x > 0 && *x < 768) {
+ ssh_set_error(session, SSH_REQUEST_DENIED,
+ "The provided value (%u) for minimal RSA key "
+ "size is too small. Use at least 768 bits.", *x);
+ return -1;
+ }
+ session->opts.rsa_min_size = *x;
+ }
+ break;
+ case SSH_OPTIONS_IDENTITY_AGENT:
+ v = value;
+ SAFE_FREE(session->opts.agent_socket);
+ if (v == NULL) {
+ /* The default value will be set by the ssh_options_apply() */
+ } else if (v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ session->opts.agent_socket = ssh_path_expand_tilde(v);
+ if (session->opts.agent_socket == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ }
+ break;
+ case SSH_OPTIONS_IDENTITIES_ONLY:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ bool *x = (bool *)value;
+ session->opts.identities_only = *x;
+ }
+ break;
+ case SSH_OPTIONS_CONTROL_MASTER:
+ if (value == NULL) {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ int *x = (int *) value;
+ if (*x < SSH_CONTROL_MASTER_NO || *x > SSH_CONTROL_MASTER_AUTOASK) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
+ session->opts.control_master = *x;
+ }
+ break;
+ case SSH_OPTIONS_CONTROL_PATH:
+ v = value;
+ if (v == NULL || v[0] == '\0') {
+ ssh_set_error_invalid(session);
+ return -1;
+ } else {
+ SAFE_FREE(session->opts.control_path);
+ rc = strcasecmp(v, "none");
+ if (rc != 0) {
+ session->opts.control_path = ssh_path_expand_tilde(v);
+ if (session->opts.control_path == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+ session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_CONTROL_PATH;
+ }
+ }
+ break;
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return -1;
@@ -1039,6 +1291,46 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
}
/**
+ * @brief This function returns the current algorithms used for algorithm
+ * negotiation. It is either libssh default, option manually set or option
+ * read from configuration file.
+ *
+ * This function will return NULL on error
+ *
+ * @param session An allocated SSH session structure.
+ * @param algo One of the ssh_kex_types_e values.
+ */
+char *ssh_options_get_algo(ssh_session session,
+ enum ssh_kex_types_e algo)
+{
+ char *value = NULL;
+
+ /* Check session and algo values are valid */
+
+ if (session == NULL) {
+ return NULL;
+ }
+
+ if (algo >= SSH_LANG_C_S) {
+ ssh_set_error_invalid(session);
+ return NULL;
+ }
+
+ /* Get the option the user has set, if there is one */
+ value = session->opts.wanted_methods[algo];
+ if (value == NULL) {
+ /* The user has not set a value, return the appropriate default */
+ if (ssh_fips_mode())
+ value = (char *)ssh_kex_get_fips_methods(algo);
+ else
+ value = (char *)ssh_kex_get_default_methods(algo);
+ }
+
+ return value;
+}
+
+
+/**
* @brief This function can get ssh the ssh port. It must only be used on
* a valid ssh session. This function is useful when the session
* options have been automatically inferred from the environment
@@ -1070,7 +1362,7 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
/**
* @brief This function can get ssh options, it does not support all options provided for
* ssh options set, but mostly those which a user-space program may care about having
- * trusted the ssh driver to infer these values from underlaying configuration files.
+ * trusted the ssh driver to infer these values from underlying configuration files.
* It operates only on those SSH_OPTIONS_* which return char*. If you wish to receive
* the port then please use ssh_options_get_port() which returns an unsigned int.
*
@@ -1090,7 +1382,7 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_IDENTITY:
* Get the first identity file name (const char *).\n
* \n
- * By default identity, id_dsa and id_rsa are checked.
+ * By default id_rsa, id_ecdsa and id_ed25519 files are used.
*
* - SSH_OPTIONS_PROXYCOMMAND:
* Get the proxycommand necessary to log into the
@@ -1103,6 +1395,46 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_KNOWNHOSTS:
* Get the path to the known_hosts file being used.
*
+ * - SSH_OPTIONS_CONTROL_PATH:
+ * Get the path to the control socket being used for connection
+ * multiplexing.
+ *
+ * - SSH_OPTIONS_KEY_EXCHANGE:
+ * Get the key exchange methods to be used. If the option has
+ * not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_HOSTKEYS:
+ * Get the preferred server host key types. If the option has
+ * not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+ * Get the preferred public key algorithms to be used for
+ * authentication.
+ *
+ * - SSH_OPTIONS_CIPHERS_C_S:
+ * Get the symmetric cipher client to server. If the option has
+ * not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_CIPHERS_S_C:
+ * Get the symmetric cipher server to client. If the option has
+ * not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_HMAC_C_S:
+ * Get the Message Authentication Code algorithm client to server
+ * If the option has not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_HMAC_S_C:
+ * Get the Message Authentication Code algorithm server to client
+ * If the option has not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_COMPRESSION_C_S:
+ * Get the compression to use for client to server communication
+ * If the option has not been set, returns the defaults.
+ *
+ * - SSH_OPTIONS_COMPRESSION_S_C:
+ * Get the compression to use for server to client communication
+ * If the option has not been set, returns the defaults.
+ *
* @param value The value to get into. As a char**, space will be
* allocated by the function for the value, it is
* your responsibility to free the memory using
@@ -1125,34 +1457,78 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
switch(type)
{
- case SSH_OPTIONS_HOST: {
+ case SSH_OPTIONS_HOST:
src = session->opts.host;
break;
- }
- case SSH_OPTIONS_USER: {
+
+ case SSH_OPTIONS_USER:
src = session->opts.username;
break;
- }
+
case SSH_OPTIONS_IDENTITY: {
- struct ssh_iterator *it = ssh_list_get_iterator(session->opts.identity);
+ struct ssh_iterator *it;
+ it = ssh_list_get_iterator(session->opts.identity);
+ if (it == NULL) {
+ it = ssh_list_get_iterator(session->opts.identity_non_exp);
+ }
if (it == NULL) {
return SSH_ERROR;
}
src = ssh_iterator_value(char *, it);
break;
}
- case SSH_OPTIONS_PROXYCOMMAND: {
+
+ case SSH_OPTIONS_PROXYCOMMAND:
src = session->opts.ProxyCommand;
break;
- }
- case SSH_OPTIONS_KNOWNHOSTS: {
+
+ case SSH_OPTIONS_KNOWNHOSTS:
src = session->opts.knownhosts;
break;
- }
- case SSH_OPTIONS_GLOBAL_KNOWNHOSTS: {
+
+ case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
src = session->opts.global_knownhosts;
break;
- }
+ case SSH_OPTIONS_CONTROL_PATH:
+ src = session->opts.control_path;
+ break;
+
+ case SSH_OPTIONS_CIPHERS_C_S:
+ src = ssh_options_get_algo(session, SSH_CRYPT_C_S);
+ break;
+
+ case SSH_OPTIONS_CIPHERS_S_C:
+ src = ssh_options_get_algo(session, SSH_CRYPT_S_C);
+ break;
+
+ case SSH_OPTIONS_KEY_EXCHANGE:
+ src = ssh_options_get_algo(session, SSH_KEX);
+ break;
+
+ case SSH_OPTIONS_HOSTKEYS:
+ src = ssh_options_get_algo(session, SSH_HOSTKEYS);
+ break;
+
+ case SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+ src = session->opts.pubkey_accepted_types;
+ break;
+
+ case SSH_OPTIONS_HMAC_C_S:
+ src = ssh_options_get_algo(session, SSH_MAC_C_S);
+ break;
+
+ case SSH_OPTIONS_HMAC_S_C:
+ src = ssh_options_get_algo(session, SSH_MAC_S_C);
+ break;
+
+ case SSH_OPTIONS_COMPRESSION_C_S:
+ src = ssh_options_get_algo(session, SSH_COMP_C_S);
+ break;
+
+ case SSH_OPTIONS_COMPRESSION_S_C:
+ src = ssh_options_get_algo(session, SSH_COMP_S_C);
+ break;
+
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return SSH_ERROR;
@@ -1175,10 +1551,10 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
* This is a helper for your application to generate the appropriate
* options from the command line arguments.\n
* The argv array and argc value are changed so that the parsed
- * arguments wont appear anymore in them.\n
+ * arguments won't appear anymore in them.\n
* The single arguments (without switches) are not parsed. thus,
* myssh -l user localhost\n
- * The command wont set the hostname value of options to localhost.
+ * The command won't set the hostname value of options to localhost.
*
* @param session The session to configure.
*
@@ -1208,8 +1584,6 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
size_t i = 0;
int argc = *argcptr;
int debuglevel = 0;
- int usersa = 0;
- int usedss = 0;
int compress = 0;
int cont = 1;
size_t current = 0;
@@ -1223,7 +1597,7 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
}
opterr = 0; /* shut up getopt */
- while((opt = getopt(argc, argv, "c:i:Cl:p:vb:rd12")) != -1) {
+ while((opt = getopt(argc, argv, "c:i:Cl:p:vb:r12")) != -1) {
switch(opt) {
case 'l':
user = optarg;
@@ -1235,10 +1609,6 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
debuglevel++;
break;
case 'r':
- usersa++;
- break;
- case 'd':
- usedss++;
break;
case 'c':
cipher = optarg;
@@ -1302,11 +1672,6 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
optind++;
}
- if (usersa && usedss) {
- ssh_set_error(session, SSH_FATAL, "Either RSA or DSS must be chosen");
- cont = 0;
- }
-
ssh_set_log_level(debuglevel);
optind = saveoptind;
@@ -1375,13 +1740,14 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
* @param session SSH session handle
*
* @param filename The options file to use, if NULL the default
- * ~/.ssh/config will be used.
+ * ~/.ssh/config and /etc/ssh/ssh_config will be used.
*
* @return 0 on success, < 0 on error.
*
* @see ssh_options_set()
*/
-int ssh_options_parse_config(ssh_session session, const char *filename) {
+int ssh_options_parse_config(ssh_session session, const char *filename)
+{
char *expanded_filename;
int r;
@@ -1426,8 +1792,8 @@ out:
return r;
}
-int ssh_options_apply(ssh_session session) {
- struct ssh_iterator *it;
+int ssh_options_apply(ssh_session session)
+{
char *tmp;
int rc;
@@ -1445,54 +1811,124 @@ int ssh_options_apply(ssh_session session) {
}
}
- if (session->opts.knownhosts == NULL) {
- tmp = ssh_path_expand_escape(session, "%d/known_hosts");
- } else {
- tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
- }
- if (tmp == NULL) {
- return -1;
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
+ if (session->opts.knownhosts == NULL) {
+ tmp = ssh_path_expand_escape(session, "%d/known_hosts");
+ } else {
+ tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
+ }
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.knownhosts);
+ session->opts.knownhosts = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
- free(session->opts.knownhosts);
- session->opts.knownhosts = tmp;
- if (session->opts.global_knownhosts == NULL) {
- tmp = strdup("/etc/ssh/ssh_known_hosts");
- } else {
- tmp = ssh_path_expand_escape(session, session->opts.global_knownhosts);
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
+ if (session->opts.global_knownhosts == NULL) {
+ tmp = strdup("/etc/ssh/ssh_known_hosts");
+ } else {
+ tmp = ssh_path_expand_escape(session,
+ session->opts.global_knownhosts);
+ }
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.global_knownhosts);
+ session->opts.global_knownhosts = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS;
}
- if (tmp == NULL) {
- return -1;
+
+
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_PROXYCOMMAND) == 0) {
+ if (session->opts.ProxyCommand != NULL) {
+ char *p = NULL;
+ size_t plen = strlen(session->opts.ProxyCommand) +
+ 5 /* strlen("exec ") */;
+
+ if (strncmp(session->opts.ProxyCommand, "exec ", 5) != 0) {
+ p = malloc(plen + 1 /* \0 */);
+ if (p == NULL) {
+ return -1;
+ }
+
+ rc = snprintf(p, plen + 1, "exec %s", session->opts.ProxyCommand);
+ if ((size_t)rc != plen) {
+ free(p);
+ return -1;
+ }
+ tmp = ssh_path_expand_escape(session, p);
+ free(p);
+ } else {
+ tmp = ssh_path_expand_escape(session,
+ session->opts.ProxyCommand);
+ }
+
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.ProxyCommand);
+ session->opts.ProxyCommand = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_PROXYCOMMAND;
+ }
}
- free(session->opts.global_knownhosts);
- session->opts.global_knownhosts = tmp;
- if (session->opts.ProxyCommand != NULL) {
- tmp = ssh_path_expand_escape(session, session->opts.ProxyCommand);
- if (tmp == NULL) {
- return -1;
+ if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_CONTROL_PATH) == 0) {
+ if (session->opts.control_path != NULL) {
+ tmp = ssh_path_expand_escape(session, session->opts.control_path);
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(session->opts.control_path);
+ session->opts.control_path = tmp;
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_CONTROL_PATH;
}
- free(session->opts.ProxyCommand);
- session->opts.ProxyCommand = tmp;
}
- for (it = ssh_list_get_iterator(session->opts.identity);
- it != NULL;
- it = it->next) {
- char *id = (char *) it->data;
- if (strncmp(id, "pkcs11:", 6) == 0) {
+ for (tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp);
+ tmp != NULL;
+ tmp = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
+ char *id = tmp;
+ if (strncmp(id, "pkcs11:", 6) != 0) {
/* PKCS#11 URIs are using percent-encoding so we can not mix
* it with ssh expansion of ssh escape characters.
- * Skip these identities now, before we will have PKCS#11 support
*/
- continue;
+ tmp = ssh_path_expand_escape(session, id);
+ if (tmp == NULL) {
+ return -1;
+ }
+ free(id);
+ }
+
+ /* use append to keep the order at first call and use prepend
+ * to put anything that comes on the nth calls to the beginning */
+ if (session->opts.exp_flags & SSH_OPT_EXP_FLAG_IDENTITY) {
+ rc = ssh_list_prepend(session->opts.identity, tmp);
+ } else {
+ rc = ssh_list_append(session->opts.identity, tmp);
}
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+ session->opts.exp_flags |= SSH_OPT_EXP_FLAG_IDENTITY;
+
+ for (tmp = ssh_list_pop_head(char *, session->opts.certificate_non_exp);
+ tmp != NULL;
+ tmp = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) {
+ char *id = tmp;
+
tmp = ssh_path_expand_escape(session, id);
if (tmp == NULL) {
return -1;
}
free(id);
- it->data = tmp;
+
+ rc = ssh_list_append(session->opts.certificate, tmp);
+ if (rc != SSH_OK) {
+ return -1;
+ }
}
return 0;
@@ -1501,12 +1937,27 @@ int ssh_options_apply(ssh_session session) {
/** @} */
#ifdef WITH_SERVER
+static bool ssh_bind_key_size_allowed(ssh_bind sshbind, ssh_key key)
+{
+ int min_size = 0;
+
+ switch (ssh_key_type(key)) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ min_size = sshbind->rsa_min_size;
+ return ssh_key_size_allowed_rsa(min_size, key);
+ default:
+ return true;
+ }
+}
+
/**
* @addtogroup libssh_server
* @{
*/
-static int ssh_bind_set_key(ssh_bind sshbind, char **key_loc,
- const void *value) {
+static int
+ssh_bind_set_key(ssh_bind sshbind, char **key_loc, const void *value)
+{
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
@@ -1523,26 +1974,12 @@ static int ssh_bind_set_key(ssh_bind sshbind, char **key_loc,
static int ssh_bind_set_algo(ssh_bind sshbind,
enum ssh_kex_types_e algo,
- const char *list)
+ const char *list,
+ char **place)
{
- char *p = NULL;
-
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(algo, list);
- } else {
- p = ssh_keep_known_algos(algo, list);
- }
- if (p == NULL) {
- ssh_set_error(sshbind, SSH_REQUEST_DENIED,
- "Setting method: no algorithm for method \"%s\" (%s)",
- ssh_kex_get_description(algo), list);
- return -1;
- }
-
- SAFE_FREE(sshbind->wanted_methods[algo]);
- sshbind->wanted_methods[algo] = p;
-
- return 0;
+ /* sshbind is needed only for ssh_set_error which takes void*
+ * the typecast is only to satisfy function parameter type */
+ return ssh_options_set_algo((ssh_session)sshbind, algo, list, place);
}
/**
@@ -1556,7 +1993,7 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* - SSH_BIND_OPTIONS_HOSTKEY:
* Set the path to an ssh host key, regardless
* of type. Only one key from per key type
- * (RSA, DSA, ECDSA) is allowed in an ssh_bind
+ * (RSA, ED25519 and ECDSA) is allowed in an ssh_bind
* at a time, and later calls to this function
* with this option for the same key type will
* override prior calls (const char *).
@@ -1580,46 +2017,52 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* - SSH_LOG_NOLOG: No logging
* - SSH_LOG_WARNING: Only warnings
* - SSH_LOG_PROTOCOL: High level protocol information
- * - SSH_LOG_PACKET: Lower level protocol infomations, packet level
+ * - SSH_LOG_PACKET: Lower level protocol information,
+ * packet level
* - SSH_LOG_FUNCTIONS: Every function path
+ * The default is SSH_LOG_NOLOG.
*
* - SSH_BIND_OPTIONS_LOG_VERBOSITY_STR:
* Set the session logging verbosity via a
* string that will be converted to a numerical
* value (e.g. "3") and interpreted according
* to the values of
- * SSH_BIND_OPTIONS_LOG_VERBOSITY above (const
- * char *).
- *
- * - SSH_BIND_OPTIONS_DSAKEY:
- * Set the path to the ssh host dsa key, SSHv2
- * only (const char *).
+ * SSH_BIND_OPTIONS_LOG_VERBOSITY above
+ * (const char *).
*
* - SSH_BIND_OPTIONS_RSAKEY:
- * Set the path to the ssh host rsa key, SSHv2
- * only (const char *).
+ * Deprecated alias to SSH_BIND_OPTIONS_HOSTKEY
+ * (const char *).
*
* - SSH_BIND_OPTIONS_ECDSAKEY:
- * Set the path to the ssh host ecdsa key,
- * SSHv2 only (const char *).
+ * Deprecated alias to SSH_BIND_OPTIONS_HOSTKEY
+ * (const char *).
*
* - SSH_BIND_OPTIONS_BANNER:
* Set the server banner sent to clients (const char *).
*
+ * - SSH_BIND_OPTIONS_DSAKEY:
+ * This is DEPRECATED, please do not use.
+ *
* - SSH_BIND_OPTIONS_IMPORT_KEY:
- * Set the Private Key for the server directly (ssh_key)
+ * Set the Private Key for the server directly
+ * (ssh_key). It will be free'd by ssh_bind_free().
+ *
+ * - SSH_BIND_OPTIONS_IMPORT_KEY_STR:
+ * Set the Private key for the server from a
+ * base64 encoded buffer (const char *).
*
* - SSH_BIND_OPTIONS_CIPHERS_C_S:
- * Set the symmetric cipher client to server (const char *,
- * comma-separated list).
+ * Set the symmetric cipher client to server
+ * (const char *, comma-separated list).
*
* - SSH_BIND_OPTIONS_CIPHERS_S_C:
- * Set the symmetric cipher server to client (const char *,
- * comma-separated list).
+ * Set the symmetric cipher server to client
+ * (const char *, comma-separated list).
*
* - SSH_BIND_OPTIONS_KEY_EXCHANGE:
- * Set the key exchange method to be used (const char *,
- * comma-separated list). ex:
+ * Set the key exchange method to be used
+ * (const char *, comma-separated list). ex:
* "ecdh-sha2-nistp256,diffie-hellman-group14-sha1"
*
* - SSH_BIND_OPTIONS_HMAC_C_S:
@@ -1656,250 +2099,237 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* set and then filtered against this list.
* (const char *, comma-separated list).
*
+ * - SSH_BIND_OPTIONS_MODULI
+ * Set the path to the moduli file. Defaults to
+ * /etc/ssh/moduli if not specified (const char *).
+ *
+ * - SSH_BIND_OPTIONS_RSA_MIN_SIZE
+ * Set the minimum RSA key size in bits to be accepted by
+ * the server for both authentication and hostkey
+ * operations. The values under 768 bits are not accepted
+ * even with this configuration option as they are
+ * considered completely broken. Setting 0 will revert
+ * the value to defaults.
+ * Default is 1024 bits or 2048 bits in FIPS mode.
+ * (int)
+ *
+ *
* @param value The value to set. This is a generic pointer and the
* datatype which should be used is described at the
* corresponding value of type above.
*
- * @return 0 on success, < 0 on error, invalid option, or parameter.
+ * @return 0 on success, < 0 on error, invalid option, or
+ * parameter.
*/
-int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
- const void *value)
+int
+ssh_bind_options_set(ssh_bind sshbind,
+ enum ssh_bind_options_e type,
+ const void *value)
{
- char *p, *q;
- const char *v;
- int i, rc;
+ bool allowed;
+ char *p, *q;
+ const char *v;
+ int i, rc;
+ char **wanted_methods = sshbind->wanted_methods;
- if (sshbind == NULL) {
- return -1;
- }
+ if (sshbind == NULL) {
+ return -1;
+ }
- switch (type) {
+ switch (type) {
+ case SSH_BIND_OPTIONS_RSAKEY:
+ case SSH_BIND_OPTIONS_ECDSAKEY:
+ /* deprecated */
case SSH_BIND_OPTIONS_HOSTKEY:
- if (value == NULL) {
- ssh_set_error_invalid(sshbind);
- return -1;
- } else {
- int key_type;
- ssh_key key;
- ssh_key *bind_key_loc = NULL;
- char **bind_key_path_loc;
-
- rc = ssh_pki_import_privkey_file(value, NULL, NULL, NULL, &key);
- if (rc != SSH_OK) {
- return -1;
- }
- key_type = ssh_key_type(key);
- switch (key_type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_DSA
- bind_key_loc = &sshbind->dsa;
- bind_key_path_loc = &sshbind->dsakey;
-#else
- ssh_set_error(sshbind,
- SSH_FATAL,
- "DSS key used and libssh compiled "
- "without DSA support");
-#endif
- break;
- case SSH_KEYTYPE_ECDSA_P256:
- case SSH_KEYTYPE_ECDSA_P384:
- case SSH_KEYTYPE_ECDSA_P521:
-#ifdef HAVE_ECC
- bind_key_loc = &sshbind->ecdsa;
- bind_key_path_loc = &sshbind->ecdsakey;
-#else
- ssh_set_error(sshbind,
- SSH_FATAL,
- "ECDSA key used and libssh compiled "
- "without ECDSA support");
-#endif
- break;
- case SSH_KEYTYPE_RSA:
- bind_key_loc = &sshbind->rsa;
- bind_key_path_loc = &sshbind->rsakey;
- break;
- case SSH_KEYTYPE_ED25519:
- bind_key_loc = &sshbind->ed25519;
- bind_key_path_loc = &sshbind->ed25519key;
- break;
- default:
- ssh_set_error(sshbind,
- SSH_FATAL,
- "Unsupported key type %d", key_type);
- }
-
- if (bind_key_loc == NULL) {
- ssh_key_free(key);
- return -1;
- }
-
- /* Set the location of the key on disk even though we don't
- need it in case some other function wants it */
- rc = ssh_bind_set_key(sshbind, bind_key_path_loc, value);
- if (rc < 0) {
- ssh_key_free(key);
- return -1;
- }
- ssh_key_free(*bind_key_loc);
- *bind_key_loc = key;
- }
- break;
case SSH_BIND_OPTIONS_IMPORT_KEY:
+ case SSH_BIND_OPTIONS_IMPORT_KEY_STR:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
int key_type;
ssh_key *bind_key_loc = NULL;
- ssh_key key = (ssh_key)value;
-
- key_type = ssh_key_type(key);
- switch (key_type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_DSA
- bind_key_loc = &sshbind->dsa;
-#else
+ ssh_key key = NULL;
+ char **bind_key_path_loc = NULL;
+
+ if (type == SSH_BIND_OPTIONS_IMPORT_KEY_STR) {
+ const char *key_str = (const char *)value;
+ rc = ssh_pki_import_privkey_base64(key_str,
+ NULL,
+ NULL,
+ NULL,
+ &key);
+ if (rc == SSH_ERROR) {
ssh_set_error(sshbind,
SSH_FATAL,
- "DSA key used and libssh compiled "
- "without DSA support");
-#endif
- break;
- case SSH_KEYTYPE_ECDSA_P256:
- case SSH_KEYTYPE_ECDSA_P384:
- case SSH_KEYTYPE_ECDSA_P521:
+ "Failed to import key from buffer");
+ return -1;
+ }
+ } else if (type == SSH_BIND_OPTIONS_IMPORT_KEY) {
+ key = (ssh_key)value;
+ } else {
+ rc = ssh_pki_import_privkey_file(value, NULL, NULL, NULL, &key);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+ allowed = ssh_bind_key_size_allowed(sshbind, key);
+ if (!allowed) {
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "The host key size %d is too small.",
+ ssh_key_size(key));
+ return -1;
+ }
+ key_type = ssh_key_type(key);
+ switch (key_type) {
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P521:
#ifdef HAVE_ECC
- bind_key_loc = &sshbind->ecdsa;
+ bind_key_loc = &sshbind->ecdsa;
+ bind_key_path_loc = &sshbind->ecdsakey;
#else
- ssh_set_error(sshbind,
- SSH_FATAL,
- "ECDSA key used and libssh compiled "
- "without ECDSA support");
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "ECDSA key used and libssh compiled "
+ "without ECDSA support");
#endif
- break;
- case SSH_KEYTYPE_RSA:
- bind_key_loc = &sshbind->rsa;
- break;
- case SSH_KEYTYPE_ED25519:
- bind_key_loc = &sshbind->ed25519;
- break;
- default:
- ssh_set_error(sshbind,
- SSH_FATAL,
- "Unsupported key type %d", key_type);
+ break;
+ case SSH_KEYTYPE_RSA:
+ bind_key_loc = &sshbind->rsa;
+ bind_key_path_loc = &sshbind->rsakey;
+ break;
+ case SSH_KEYTYPE_ED25519:
+ bind_key_loc = &sshbind->ed25519;
+ bind_key_path_loc = &sshbind->ed25519key;
+ break;
+ default:
+ ssh_set_error(sshbind,
+ SSH_FATAL,
+ "Unsupported key type %d",
+ key_type);
+ }
+ if (type == SSH_BIND_OPTIONS_RSAKEY ||
+ type == SSH_BIND_OPTIONS_ECDSAKEY ||
+ type == SSH_BIND_OPTIONS_HOSTKEY) {
+ if (bind_key_loc == NULL) {
+ ssh_key_free(key);
+ return -1;
+ }
+ /* Set the location of the key on disk even though we don't
+ need it in case some other function wants it */
+ rc = ssh_bind_set_key(sshbind, bind_key_path_loc, value);
+ if (rc < 0) {
+ ssh_key_free(key);
+ return -1;
+ }
+ } else {
+ if (bind_key_loc == NULL) {
+ return -1;
+ }
}
- if (bind_key_loc == NULL)
- return -1;
ssh_key_free(*bind_key_loc);
*bind_key_loc = key;
}
break;
case SSH_BIND_OPTIONS_BINDADDR:
- if (value == NULL) {
- ssh_set_error_invalid(sshbind);
- return -1;
- } else {
- SAFE_FREE(sshbind->bindaddr);
- sshbind->bindaddr = strdup(value);
- if (sshbind->bindaddr == NULL) {
- ssh_set_error_oom(sshbind);
- return -1;
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return -1;
+ } else {
+ SAFE_FREE(sshbind->bindaddr);
+ sshbind->bindaddr = strdup(value);
+ if (sshbind->bindaddr == NULL) {
+ ssh_set_error_oom(sshbind);
+ return -1;
+ }
}
- }
- break;
+ break;
case SSH_BIND_OPTIONS_BINDPORT:
- if (value == NULL) {
- ssh_set_error_invalid(sshbind);
- return -1;
- } else {
- int *x = (int *) value;
- sshbind->bindport = *x & 0xffffU;
- }
- break;
- case SSH_BIND_OPTIONS_BINDPORT_STR:
- if (value == NULL) {
- sshbind->bindport = 22 & 0xffffU;
- } else {
- q = strdup(value);
- if (q == NULL) {
- ssh_set_error_oom(sshbind);
- return -1;
- }
- i = strtol(q, &p, 10);
- if (q == p) {
- SAFE_FREE(q);
- }
- SAFE_FREE(q);
-
- sshbind->bindport = i & 0xffffU;
- }
- break;
- case SSH_BIND_OPTIONS_LOG_VERBOSITY:
- if (value == NULL) {
- ssh_set_error_invalid(sshbind);
- return -1;
- } else {
- int *x = (int *) value;
- ssh_set_log_level(*x & 0xffffU);
- }
- break;
- case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR:
- if (value == NULL) {
- ssh_set_log_level(0);
- } else {
- q = strdup(value);
- if (q == NULL) {
- ssh_set_error_oom(sshbind);
- return -1;
- }
- i = strtol(q, &p, 10);
- if (q == p) {
- SAFE_FREE(q);
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return -1;
+ } else {
+ int *x = (int *)value;
+ sshbind->bindport = *x & 0xffffU;
}
- SAFE_FREE(q);
+ break;
+ case SSH_BIND_OPTIONS_BINDPORT_STR:
+ if (value == NULL) {
+ sshbind->bindport = 22 & 0xffffU;
+ } else {
+ q = strdup(value);
+ if (q == NULL) {
+ ssh_set_error_oom(sshbind);
+ return -1;
+ }
+ i = strtol(q, &p, 10);
+ if (q == p) {
+ SSH_LOG(SSH_LOG_DEBUG, "No bind port was parsed");
+ SAFE_FREE(q);
+ return -1;
+ }
+ SAFE_FREE(q);
- ssh_set_log_level(i & 0xffffU);
- }
- break;
- case SSH_BIND_OPTIONS_DSAKEY:
- rc = ssh_bind_set_key(sshbind, &sshbind->dsakey, value);
- if (rc < 0) {
- return -1;
+ sshbind->bindport = i & 0xffffU;
}
break;
- case SSH_BIND_OPTIONS_RSAKEY:
- rc = ssh_bind_set_key(sshbind, &sshbind->rsakey, value);
- if (rc < 0) {
+ case SSH_BIND_OPTIONS_LOG_VERBOSITY:
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
return -1;
+ } else {
+ int *x = (int *)value;
+ ssh_set_log_level(*x & 0xffffU);
}
break;
- case SSH_BIND_OPTIONS_ECDSAKEY:
- rc = ssh_bind_set_key(sshbind, &sshbind->ecdsakey, value);
- if (rc < 0) {
- return -1;
+ case SSH_BIND_OPTIONS_LOG_VERBOSITY_STR:
+ if (value == NULL) {
+ ssh_set_log_level(0);
+ } else {
+ q = strdup(value);
+ if (q == NULL) {
+ ssh_set_error_oom(sshbind);
+ return -1;
+ }
+ i = strtol(q, &p, 10);
+ if (q == p) {
+ SSH_LOG(SSH_LOG_DEBUG, "No log verbositiy was parsed");
+ SAFE_FREE(q);
+ return -1;
+ }
+ SAFE_FREE(q);
+
+ ssh_set_log_level(i & 0xffffU);
}
break;
case SSH_BIND_OPTIONS_BANNER:
- if (value == NULL) {
- ssh_set_error_invalid(sshbind);
- return -1;
- } else {
- SAFE_FREE(sshbind->banner);
- sshbind->banner = strdup(value);
- if (sshbind->banner == NULL) {
- ssh_set_error_oom(sshbind);
- return -1;
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return -1;
+ } else {
+ SAFE_FREE(sshbind->banner);
+ sshbind->banner = strdup(value);
+ if (sshbind->banner == NULL) {
+ ssh_set_error_oom(sshbind);
+ return -1;
+ }
}
- }
- break;
+ break;
case SSH_BIND_OPTIONS_CIPHERS_C_S:
v = value;
if (v == NULL || v[0] == '\0') {
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_CRYPT_C_S, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_CRYPT_C_S,
+ v,
+ &wanted_methods[SSH_CRYPT_C_S]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_CIPHERS_S_C:
@@ -1908,8 +2338,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_CRYPT_S_C, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_CRYPT_S_C,
+ v,
+ &wanted_methods[SSH_CRYPT_S_C]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_KEY_EXCHANGE:
@@ -1918,7 +2353,10 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- rc = ssh_bind_set_algo(sshbind, SSH_KEX, v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_KEX,
+ v,
+ &wanted_methods[SSH_KEX]);
if (rc < 0) {
return -1;
}
@@ -1930,18 +2368,28 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_MAC_C_S, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_MAC_C_S,
+ v,
+ &wanted_methods[SSH_MAC_C_S]);
+ if (rc < 0) {
return -1;
+ }
}
break;
- case SSH_BIND_OPTIONS_HMAC_S_C:
+ case SSH_BIND_OPTIONS_HMAC_S_C:
v = value;
if (v == NULL || v[0] == '\0') {
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_bind_set_algo(sshbind, SSH_MAC_S_C, v) < 0)
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_MAC_S_C,
+ v,
+ &wanted_methods[SSH_MAC_S_C]);
+ if (rc < 0) {
return -1;
+ }
}
break;
case SSH_BIND_OPTIONS_CONFIG_DIR:
@@ -1966,20 +2414,13 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- if (ssh_fips_mode()) {
- p = ssh_keep_fips_algos(SSH_HOSTKEYS, v);
- } else {
- p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
- }
- if (p == NULL) {
- ssh_set_error(sshbind, SSH_REQUEST_DENIED,
- "Setting method: no known public key algorithm (%s)",
- v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_HOSTKEYS,
+ v,
+ &sshbind->pubkey_accepted_key_types);
+ if (rc < 0) {
return -1;
}
-
- SAFE_FREE(sshbind->pubkey_accepted_key_types);
- sshbind->pubkey_accepted_key_types = p;
}
break;
case SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS:
@@ -1988,7 +2429,10 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
ssh_set_error_invalid(sshbind);
return -1;
} else {
- rc = ssh_bind_set_algo(sshbind, SSH_HOSTKEYS, v);
+ rc = ssh_bind_set_algo(sshbind,
+ SSH_HOSTKEYS,
+ v,
+ &wanted_methods[SSH_HOSTKEYS]);
if (rc < 0) {
return -1;
}
@@ -2003,19 +2447,53 @@ int ssh_bind_options_set(ssh_bind sshbind, enum ssh_bind_options_e type,
sshbind->config_processed = !(*x);
}
break;
+ case SSH_BIND_OPTIONS_MODULI:
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return -1;
+ } else {
+ SAFE_FREE(sshbind->moduli_file);
+ sshbind->moduli_file = strdup(value);
+ if (sshbind->moduli_file == NULL) {
+ ssh_set_error_oom(sshbind);
+ return -1;
+ }
+ }
+ break;
+ case SSH_BIND_OPTIONS_RSA_MIN_SIZE:
+ if (value == NULL) {
+ ssh_set_error_invalid(sshbind);
+ return -1;
+ } else {
+ int *x = (int *)value;
+ if (*x > 0 && *x < 768) {
+ ssh_set_error(sshbind,
+ SSH_REQUEST_DENIED,
+ "The provided value (%u) for minimal RSA key "
+ "size is too small. Use at least 768 bits.",
+ *x);
+ return -1;
+ }
+ sshbind->rsa_min_size = *x;
+ }
+ break;
default:
- ssh_set_error(sshbind, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
- return -1;
- break;
- }
+ ssh_set_error(sshbind,
+ SSH_REQUEST_DENIED,
+ "Unknown ssh option %d",
+ type);
+ return -1;
+ break;
+ }
- return 0;
+ return 0;
}
static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
{
- char buf[MAX_BUF_SIZE];
- char *r, *x = NULL;
+ char *buf = NULL;
+ char *r = NULL;
+ char *x = NULL;
const char *p;
size_t i, l;
@@ -2031,6 +2509,13 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
return NULL;
}
+ buf = malloc(MAX_BUF_SIZE);
+ if (buf == NULL) {
+ ssh_set_error_oom(sshbind);
+ free(r);
+ return NULL;
+ }
+
p = r;
buf[0] = '\0';
@@ -2039,6 +2524,7 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
buf[i] = *p;
i++;
if (i >= MAX_BUF_SIZE) {
+ free(buf);
free(r);
return NULL;
}
@@ -2058,12 +2544,14 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
default:
ssh_set_error(sshbind, SSH_FATAL,
"Wrong escape sequence detected");
+ free(buf);
free(r);
return NULL;
}
if (x == NULL) {
ssh_set_error_oom(sshbind);
+ free(buf);
free(r);
return NULL;
}
@@ -2072,18 +2560,26 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
if (i >= MAX_BUF_SIZE) {
ssh_set_error(sshbind, SSH_FATAL,
"String too long");
+ free(buf);
free(x);
free(r);
return NULL;
}
l = strlen(buf);
- strncpy(buf + l, x, sizeof(buf) - l - 1);
+ strncpy(buf + l, x, MAX_BUF_SIZE - l - 1);
buf[i] = '\0';
SAFE_FREE(x);
}
free(r);
- return strdup(buf);
+
+ /* strip the unused space by realloc */
+ x = realloc(buf, strlen(buf) + 1);
+ if (x == NULL) {
+ ssh_set_error_oom(sshbind);
+ free(buf);
+ }
+ return x;
}
/**
@@ -2096,7 +2592,7 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s)
* @param sshbind SSH bind handle
*
* @param filename The options file to use; if NULL only the global
- * configuration is parsed and applied (if it haven't been
+ * configuration is parsed and applied (if it hasn't been
* processed before).
*
* @return 0 on success, < 0 on error.
diff --git a/src/packet.c b/src/packet.c
index f14731c9..8508c731 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -113,7 +113,7 @@ static ssh_packet_callback default_packet_handlers[]= {
ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80
#else /* WITH_SERVER */
NULL,
-#endif /* WITH_SERVER */
+#endif /* WITH_SERVER */
ssh_request_success, // SSH2_MSG_REQUEST_SUCCESS 81
ssh_request_denied, // SSH2_MSG_REQUEST_FAILURE 82
NULL, NULL, NULL, NULL, NULL, NULL, NULL,// 83-89
@@ -363,9 +363,14 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* Transitions:
* - session->dh_handshake_state = DH_STATE_INIT_SENT
* then calls dh_handshake_server which triggers:
- * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+ * - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
+ if (!session->server) {
+ rc = SSH_PACKET_DENIED;
+ break;
+ }
+
if (session->session_state != SSH_SESSION_STATE_DH) {
rc = SSH_PACKET_DENIED;
break;
@@ -391,7 +396,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* or dh_handshake_state == DH_STATE_REQUEST_SENT (dh-gex)
*
* Transitions:
- * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+ * - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
if (session->session_state != SSH_SESSION_STATE_DH) {
@@ -425,7 +430,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
- * - dh_hanshake_state == DH_STATE_FINISHED
+ * - dh_handshake_state == DH_STATE_FINISHED
*
* Transitions:
* - if authentication was successful:
@@ -454,7 +459,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
- * - dh_hanshake_state == DH_STATE_FINISHED
+ * - dh_handshake_state == DH_STATE_FINISHED
* - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
@@ -492,7 +497,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
- * - dh_hanshake_state == DH_STATE_FINISHED
+ * - dh_handshake_state == DH_STATE_FINISHED
* - session->auth.state == SSH_AUTH_STATE_KBDINT_SENT
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
* or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
@@ -688,10 +693,12 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_ACCEPTED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -699,21 +706,18 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
break;
}
- if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
- rc = SSH_PACKET_DENIED;
- break;
- }
-
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_REQUEST_FAILURE: // 82
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - session->global_req_state == SSH_CHANNEL_REQ_STATE_DENIED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -721,11 +725,6 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
break;
}
- if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
- rc = SSH_PACKET_DENIED;
- break;
- }
-
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_CHANNEL_OPEN: // 90
@@ -878,10 +877,12 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -895,10 +896,12 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATED
- * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
*
* Transitions:
- * - channel->request_state = SSH_CHANNEL_REQ_STATE_DENIED
+ * - From channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+ * - To channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+ *
+ * If not in a pending state, message is ignored in the callback handler.
* */
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
@@ -1054,24 +1057,26 @@ static bool ssh_packet_need_rekey(ssh_session session,
* @param user pointer to current ssh_session
* @param data pointer to the data received
* @len length of data received. It might not be enough for a complete packet
- * @returns number of bytes read and processed.
+ * @returns number of bytes read and processed. Zero means only partial packet
+ * received and negative value means error.
*/
-int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
+size_t
+ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
{
ssh_session session = (ssh_session)user;
uint32_t blocksize = 8;
uint32_t lenfield_blocksize = 8;
size_t current_macsize = 0;
uint8_t *ptr = NULL;
- int to_be_read;
+ long to_be_read;
int rc;
uint8_t *cleartext_packet = NULL;
uint8_t *packet_second_block = NULL;
uint8_t *mac = NULL;
- size_t packet_remaining;
+ size_t packet_remaining, packet_offset;
uint32_t packet_len, compsize, payloadsize;
uint8_t padding;
- size_t processed = 0; /* number of byte processed from the callback */
+ size_t processed = 0; /* number of bytes processed from the callback */
enum ssh_packet_filter_result_e filter_result;
struct ssh_crypto_struct *crypto = NULL;
bool etm = false;
@@ -1114,7 +1119,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->packet_state == PACKET_STATE_PROCESSING ?
"PROCESSING" : "unknown");
#endif
- switch(session->packet_state) {
+ switch (session->packet_state) {
case PACKET_STATE_INIT:
if (receivedlen < lenfield_blocksize + etm_packet_offset) {
/*
@@ -1147,11 +1152,13 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
if (!etm) {
- ptr = ssh_buffer_allocate(session->in_buffer, lenfield_blocksize);
+ ptr = ssh_buffer_allocate(session->in_buffer,
+ lenfield_blocksize);
if (ptr == NULL) {
goto error;
}
- packet_len = ssh_packet_decrypt_len(session, ptr, (uint8_t *)data);
+ packet_len = ssh_packet_decrypt_len(session, ptr,
+ (uint8_t *)data);
to_be_read = packet_len - lenfield_blocksize + sizeof(uint32_t);
} else {
/* Length is unencrypted in case of Encrypt-then-MAC */
@@ -1163,7 +1170,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
if (packet_len > MAX_PACKET_LEN) {
ssh_set_error(session,
SSH_FATAL,
- "read_packet(): Packet len too high(%u %.4x)",
+ "read_packet(): Packet len too high(%" PRIu32 " %.4" PRIx32 ")",
packet_len, packet_len);
goto error;
}
@@ -1171,7 +1178,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
/* remote sshd sends invalid sizes? */
ssh_set_error(session,
SSH_FATAL,
- "Given numbers of bytes left to be read < 0 (%d)!",
+ "Given numbers of bytes left to be read < 0 (%ld)!",
to_be_read);
goto error;
}
@@ -1181,28 +1188,27 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
FALL_THROUGH;
case PACKET_STATE_SIZEREAD:
packet_len = session->in_packet.len;
- processed = lenfield_blocksize + etm_packet_offset;
+ packet_offset = processed = lenfield_blocksize + etm_packet_offset;
to_be_read = packet_len + sizeof(uint32_t) + current_macsize;
/* if to_be_read is zero, the whole packet was blocksize bytes. */
if (to_be_read != 0) {
- if (receivedlen < (unsigned int)to_be_read) {
+ if (receivedlen < (unsigned long)to_be_read) {
/* give up, not enough data in buffer */
SSH_LOG(SSH_LOG_PACKET,
"packet: partial packet (read len) "
- "[len=%d, receivedlen=%d, to_be_read=%d]",
+ "[len=%" PRIu32 ", receivedlen=%zu, to_be_read=%ld]",
packet_len,
- (int)receivedlen,
+ receivedlen,
to_be_read);
return 0;
}
- packet_second_block = (uint8_t*)data + lenfield_blocksize + etm_packet_offset;
+ packet_second_block = (uint8_t*)data + packet_offset;
processed = to_be_read - current_macsize;
}
/* remaining encrypted bytes from the packet, MAC not included */
- packet_remaining =
- packet_len - (lenfield_blocksize - sizeof(uint32_t) + etm_packet_offset);
+ packet_remaining = packet_len - (packet_offset - sizeof(uint32_t));
cleartext_packet = ssh_buffer_allocate(session->in_buffer,
packet_remaining);
if (cleartext_packet == NULL) {
@@ -1225,16 +1231,16 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
}
/*
- * Decrypt the packet. In case of EtM mode, the length is already
- * known as it's unencrypted. In the other case, lenfield_blocksize bytes
- * already have been decrypted.
+ * Decrypt the packet. In case of EtM mode, the length is
+ * already known as it's unencrypted. In the other case,
+ * lenfield_blocksize bytes already have been decrypted.
*/
if (packet_remaining > 0) {
rc = ssh_packet_decrypt(session,
cleartext_packet,
(uint8_t *)data,
- lenfield_blocksize + etm_packet_offset,
- processed - (lenfield_blocksize + etm_packet_offset));
+ packet_offset,
+ processed - packet_offset);
if (rc < 0) {
ssh_set_error(session,
SSH_FATAL,
@@ -1244,9 +1250,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
if (crypto->in_hmac != SSH_HMAC_NONE && !etm) {
+ ssh_buffer in = session->in_buffer;
rc = ssh_packet_hmac_verify(session,
- ssh_buffer_get(session->in_buffer),
- ssh_buffer_get_len(session->in_buffer),
+ ssh_buffer_get(in),
+ ssh_buffer_get_len(in),
mac,
crypto->in_hmac);
if (rc < 0) {
@@ -1288,7 +1295,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
if (padding > ssh_buffer_get_len(session->in_buffer)) {
ssh_set_error(session,
SSH_FATAL,
- "Invalid padding: %d (%d left)",
+ "Invalid padding: %d (%" PRIu32 " left)",
padding,
ssh_buffer_get_len(session->in_buffer));
goto error;
@@ -1297,15 +1304,29 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
compsize = ssh_buffer_get_len(session->in_buffer);
#ifdef WITH_ZLIB
- if (crypto && crypto->do_compress_in
- && ssh_buffer_get_len(session->in_buffer) > 0) {
- rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN);
+ if (crypto && crypto->do_compress_in &&
+ ssh_buffer_get_len(session->in_buffer) > 0) {
+ rc = decompress_buffer(session, session->in_buffer,
+ MAX_PACKET_LEN);
if (rc < 0) {
goto error;
}
}
#endif /* WITH_ZLIB */
payloadsize = ssh_buffer_get_len(session->in_buffer);
+ if (session->recv_seq == UINT32_MAX) {
+ /* Overflowing sequence numbers is always fishy */
+ if (crypto == NULL) {
+ /* don't allow sequence number overflow when unencrypted */
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Incoming sequence number overflow");
+ goto error;
+ } else {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Incoming sequence number overflow");
+ }
+ }
session->recv_seq++;
if (crypto != NULL) {
struct ssh_cipher_struct *cipher = NULL;
@@ -1326,13 +1347,27 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->packet_state = PACKET_STATE_PROCESSING;
ssh_packet_parse_type(session);
SSH_LOG(SSH_LOG_PACKET,
- "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]",
- session->in_packet.type, packet_len, padding, compsize, payloadsize);
+ "packet: read type %hhd [len=%" PRIu32 ",padding=%hhd,"
+ "comp=%" PRIu32 ",payload=%" PRIu32 "]",
+ session->in_packet.type, packet_len, padding, compsize,
+ payloadsize);
+ if (crypto == NULL) {
+ /* In strict kex, only a few packets are allowed. Taint the session
+ * if we received packets that are normally allowed but to be
+ * refused if we are in strict kex when KEX is over.
+ */
+ uint8_t type = session->in_packet.type;
+ if (type != SSH2_MSG_KEXINIT && type != SSH2_MSG_NEWKEYS &&
+ (type < SSH2_MSG_KEXDH_INIT ||
+ type > SSH2_MSG_KEX_DH_GEX_REQUEST)) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
+ }
/* Check if the packet is expected */
filter_result = ssh_packet_incoming_filter(session);
- switch(filter_result) {
+ switch (filter_result) {
case SSH_PACKET_ALLOWED:
/* Execute callbacks */
ssh_packet_process(session, session->in_packet.type);
@@ -1344,6 +1379,9 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->in_packet.type);
goto error;
case SSH_PACKET_UNKNOWN:
+ if (crypto == NULL) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
ssh_packet_send_unimplemented(session, session->recv_seq - 1);
break;
}
@@ -1357,7 +1395,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
ptr = ((uint8_t*)data) + processed;
- rc = ssh_packet_socket_callback(ptr, receivedlen - processed,user);
+ rc = ssh_packet_socket_callback(ptr, receivedlen - processed,
+ user);
processed += rc;
}
@@ -1383,8 +1422,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
session->packet_state);
error:
- session->session_state= SSH_SESSION_STATE_ERROR;
- SSH_LOG(SSH_LOG_PACKET,"Packet: processed %zu bytes", processed);
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ SSH_LOG(SSH_LOG_PACKET, "Packet: processed %zu bytes", processed);
return processed;
}
@@ -1412,31 +1451,41 @@ static void ssh_packet_socket_controlflow_callback(int code, void *userdata)
}
}
-void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){
- session->socket_callbacks.data=ssh_packet_socket_callback;
- session->socket_callbacks.connected=NULL;
- session->socket_callbacks.controlflow = ssh_packet_socket_controlflow_callback;
- session->socket_callbacks.userdata=session;
- ssh_socket_set_callbacks(s,&session->socket_callbacks);
+void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s)
+{
+ struct ssh_socket_callbacks_struct *callbacks = &session->socket_callbacks;
+
+ callbacks->data = ssh_packet_socket_callback;
+ callbacks->connected = NULL;
+ callbacks->controlflow = ssh_packet_socket_controlflow_callback;
+ callbacks->userdata = session;
+ ssh_socket_set_callbacks(s, callbacks);
}
/** @internal
* @brief sets the callbacks for the packet layer
*/
-void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
- if(session->packet_callbacks == NULL){
- session->packet_callbacks = ssh_list_new();
- }
- if (session->packet_callbacks != NULL) {
+void
+ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks)
+{
+ if (session->packet_callbacks == NULL) {
+ session->packet_callbacks = ssh_list_new();
+ if (session->packet_callbacks == NULL) {
+ ssh_set_error_oom(session);
+ return;
+ }
+ }
ssh_list_append(session->packet_callbacks, callbacks);
- }
}
/** @internal
* @brief remove the callbacks from the packet layer
*/
-void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
+void
+ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callbacks)
+{
struct ssh_iterator *it = NULL;
+
it = ssh_list_find(session->packet_callbacks, callbacks);
if (it != NULL) {
ssh_list_remove(session->packet_callbacks, it);
@@ -1446,12 +1495,15 @@ void ssh_packet_remove_callbacks(ssh_session session, ssh_packet_callbacks callb
/** @internal
* @brief sets the default packet handlers
*/
-void ssh_packet_set_default_callbacks(ssh_session session){
- session->default_packet_callbacks.start=1;
- session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback);
- session->default_packet_callbacks.user=session;
- session->default_packet_callbacks.callbacks=default_packet_handlers;
- ssh_packet_set_callbacks(session, &session->default_packet_callbacks);
+void ssh_packet_set_default_callbacks(ssh_session session)
+{
+ struct ssh_packet_callbacks_struct *c = &session->default_packet_callbacks;
+
+ c->start = 1;
+ c->n_callbacks = sizeof(default_packet_handlers) / sizeof(ssh_packet_callback);
+ c->user = session;
+ c->callbacks = default_packet_handlers;
+ ssh_packet_set_callbacks(session, c);
}
/** @internal
@@ -1505,7 +1557,33 @@ void ssh_packet_process(ssh_session session, uint8_t type)
SSH_LOG(SSH_LOG_RARE, "Failed to send unimplemented: %s",
ssh_get_error(session));
}
+ if (session->current_crypto == NULL) {
+ session->flags |= SSH_SESSION_FLAG_KEX_TAINTED;
+ }
+ }
+}
+
+/** @internal
+ * @brief sends a SSH_MSG_NEWKEYS when enabling the new negotiated ciphers
+ * @param session the SSH session
+ * @return SSH_ERROR on error, else SSH_OK
+ */
+int ssh_packet_send_newkeys(ssh_session session)
+{
+ int rc;
+
+ /* Send the MSG_NEWKEYS */
+ rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS);
+ if (rc < 0) {
+ return rc;
}
+
+ rc = ssh_packet_send(session);
+ if (rc == SSH_ERROR) {
+ return rc;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent");
+ return rc;
}
/** @internal
@@ -1543,12 +1621,12 @@ SSH_PACKET_CALLBACK(ssh_packet_unimplemented){
rc = ssh_buffer_unpack(packet, "d", &seq);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARNING,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not unpack SSH_MSG_UNIMPLEMENTED packet");
}
SSH_LOG(SSH_LOG_RARE,
- "Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq);
+ "Received SSH_MSG_UNIMPLEMENTED (sequence number %" PRIu32 ")",seq);
return SSH_PACKET_USED;
}
@@ -1714,8 +1792,8 @@ static int packet_send2(ssh_session session)
}
SSH_LOG(SSH_LOG_PACKET,
- "packet: wrote [type=%u, len=%u, padding_size=%hhd, comp=%u, "
- "payload=%u]",
+ "packet: wrote [type=%u, len=%" PRIu32 ", padding_size=%hhd, comp=%" PRIu32 ", "
+ "payload=%" PRIu32 "]",
type,
finallen,
padding_size,
@@ -1755,10 +1833,12 @@ static bool
ssh_packet_in_rekey(ssh_session session)
{
/* We know we are rekeying if we are authenticated and the DH
- * status is not finished
+ * status is not finished, but we only queue packets until we've
+ * sent our NEWKEYS.
*/
return (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) &&
- (session->dh_handshake_state != DH_STATE_FINISHED);
+ (session->dh_handshake_state != DH_STATE_FINISHED) &&
+ (session->dh_handshake_state != DH_STATE_NEWKEYS_SENT);
}
int ssh_packet_send(ssh_session session)
@@ -1799,7 +1879,7 @@ int ssh_packet_send(ssh_session session)
if (need_rekey) {
/* Send the KEXINIT packet instead.
- * This recursivelly calls the packet_send(), but it should
+ * This recursively calls the packet_send(), but it should
* not get into rekeying again.
* After that we need to handle the key exchange responses
* up to the point where we can send the rest of the queue.
@@ -1816,6 +1896,10 @@ int ssh_packet_send(ssh_session session)
if (rc == SSH_OK && type == SSH2_MSG_NEWKEYS) {
struct ssh_iterator *it;
+ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) {
+ /* reset packet sequence number when running in strict kex mode */
+ session->send_seq = 0;
+ }
for (it = ssh_list_get_iterator(session->out_queue);
it != NULL;
it = ssh_list_get_iterator(session->out_queue)) {
@@ -1867,7 +1951,7 @@ ssh_init_rekey_state(struct ssh_session_struct *session,
session->opts.rekey_data / cipher->blocksize);
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"Set rekey after %" PRIu64 " blocks",
cipher->max_blocks);
}
@@ -1895,7 +1979,7 @@ ssh_packet_set_newkeys(ssh_session session,
session->next_crypto->used |= direction;
if (session->current_crypto != NULL) {
if (session->current_crypto->used & direction) {
- SSH_LOG(SSH_LOG_WARNING, "This direction isn't used anymore.");
+ SSH_LOG(SSH_LOG_TRACE, "This direction isn't used anymore.");
}
/* Mark the current requested direction unused */
session->current_crypto->used &= ~direction;
@@ -1903,7 +1987,7 @@ ssh_packet_set_newkeys(ssh_session session,
/* Both sides switched: do the actual switch now */
if (session->next_crypto->used == SSH_DIRECTION_BOTH) {
- size_t digest_len;
+ size_t session_id_len;
if (session->current_crypto != NULL) {
crypto_free(session->current_crypto);
@@ -1920,8 +2004,8 @@ ssh_packet_set_newkeys(ssh_session session,
return SSH_ERROR;
}
- digest_len = session->current_crypto->digest_len;
- session->next_crypto->session_id = malloc(digest_len);
+ session_id_len = session->current_crypto->session_id_len;
+ session->next_crypto->session_id = malloc(session_id_len);
if (session->next_crypto->session_id == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
@@ -1929,7 +2013,8 @@ ssh_packet_set_newkeys(ssh_session session,
memcpy(session->next_crypto->session_id,
session->current_crypto->session_id,
- digest_len);
+ session_id_len);
+ session->next_crypto->session_id_len = session_id_len;
return SSH_OK;
}
@@ -1968,7 +2053,7 @@ ssh_packet_set_newkeys(ssh_session session,
ssh_init_rekey_state(session, in_cipher);
if (session->opts.rekey_time != 0) {
ssh_timestamp_init(&session->last_rekey_time);
- SSH_LOG(SSH_LOG_PROTOCOL, "Set rekey after %" PRIu32 " seconds",
+ SSH_LOG(SSH_LOG_DEBUG, "Set rekey after %" PRIu32 " seconds",
session->opts.rekey_time/1000);
}
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 4e692915..7edb6791 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -45,36 +45,48 @@
*
* @brief Handle a SSH_DISCONNECT packet.
*/
-SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){
- int rc;
- uint32_t code = 0;
- char *error = NULL;
- ssh_string error_s;
- (void)user;
- (void)type;
-
- rc = ssh_buffer_get_u32(packet, &code);
- if (rc != 0) {
- code = ntohl(code);
- }
-
- error_s = ssh_buffer_get_ssh_string(packet);
- if (error_s != NULL) {
- error = ssh_string_to_char(error_s);
- SSH_STRING_FREE(error_s);
- }
- SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",
- code, error != NULL ? error : "no error");
- ssh_set_error(session, SSH_FATAL,
- "Received SSH_MSG_DISCONNECT: %d:%s",
- code, error != NULL ? error : "no error");
- SAFE_FREE(error);
-
- ssh_socket_close(session->socket);
- session->alive = 0;
- session->session_state = SSH_SESSION_STATE_ERROR;
- /* TODO: handle a graceful disconnect */
- return SSH_PACKET_USED;
+SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback)
+{
+ int rc;
+ uint32_t code = 0;
+ char *error = NULL;
+ ssh_string error_s = NULL;
+
+ (void)user;
+ (void)type;
+
+ rc = ssh_buffer_get_u32(packet, &code);
+ if (rc != 0) {
+ code = ntohl(code);
+ }
+
+ error_s = ssh_buffer_get_ssh_string(packet);
+ if (error_s != NULL) {
+ error = ssh_string_to_char(error_s);
+ SSH_STRING_FREE(error_s);
+ }
+
+ if (error != NULL) {
+ session->peer_discon_msg = strdup(error);
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Received SSH_MSG_DISCONNECT %" PRIu32 ":%s",
+ code,
+ error != NULL ? error : "no error");
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Received SSH_MSG_DISCONNECT: %" PRIu32 ":%s",
+ code,
+ error != NULL ? error : "no error");
+ SAFE_FREE(error);
+
+ ssh_session_socket_close(session);
+ /* correctly handle disconnect during authorization */
+ session->auth.state = SSH_AUTH_STATE_FAILED;
+
+ /* TODO: handle a graceful disconnect */
+ return SSH_PACKET_USED;
}
/**
@@ -82,96 +94,125 @@ SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){
*
* @brief Handle a SSH_IGNORE and SSH_DEBUG packet.
*/
-SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){
+SSH_PACKET_CALLBACK(ssh_packet_ignore_callback)
+{
(void)session; /* unused */
- (void)user;
- (void)type;
- (void)packet;
- SSH_LOG(SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG");
- /* TODO: handle a graceful disconnect */
- return SSH_PACKET_USED;
+ (void)user;
+ (void)type;
+ (void)packet;
+
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Received %s packet",
+ type == SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG");
+
+ /* TODO: handle a graceful disconnect */
+ return SSH_PACKET_USED;
}
-SSH_PACKET_CALLBACK(ssh_packet_newkeys){
- ssh_string sig_blob = NULL;
- ssh_signature sig = NULL;
- int rc;
- (void)packet;
- (void)user;
- (void)type;
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS");
-
- if (session->session_state != SSH_SESSION_STATE_DH ||
- session->dh_handshake_state != DH_STATE_NEWKEYS_SENT) {
- ssh_set_error(session,
- SSH_FATAL,
- "ssh_packet_newkeys called in wrong state : %d:%d",
- session->session_state,session->dh_handshake_state);
- goto error;
- }
-
- if(session->server){
- /* server things are done in server.c */
- session->dh_handshake_state=DH_STATE_FINISHED;
- } else {
- ssh_key server_key;
-
- /* client */
-
- /* Verify the host's signature. FIXME do it sooner */
- sig_blob = session->next_crypto->dh_server_signature;
- session->next_crypto->dh_server_signature = NULL;
-
- /* get the server public key */
- server_key = ssh_dh_get_next_server_publickey(session);
- if (server_key == NULL) {
- goto error;
- }
+SSH_PACKET_CALLBACK(ssh_packet_newkeys)
+{
+ ssh_string sig_blob = NULL;
+ ssh_signature sig = NULL;
+ int rc;
+
+ (void)packet;
+ (void)user;
+ (void)type;
- rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
- if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_NEWKEYS");
+
+ if (session->session_state != SSH_SESSION_STATE_DH ||
+ session->dh_handshake_state != DH_STATE_NEWKEYS_SENT) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "ssh_packet_newkeys called in wrong state : %d:%d",
+ session->session_state,
+ session->dh_handshake_state);
goto error;
}
- /* Check if signature from server matches user preferences */
- if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
- if (!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
- sig->type_c)) {
+ if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) {
+ /* reset packet sequence number when running in strict kex mode */
+ session->recv_seq = 0;
+ /* Check that we aren't tainted */
+ if (session->flags & SSH_SESSION_FLAG_KEX_TAINTED) {
ssh_set_error(session,
SSH_FATAL,
- "Public key from server (%s) doesn't match user "
- "preference (%s)",
- sig->type_c,
- session->opts.wanted_methods[SSH_HOSTKEYS]);
+ "Received unexpected packets in strict KEX mode.");
goto error;
}
}
- rc = ssh_pki_signature_verify(session,
- sig,
- server_key,
- session->next_crypto->secret_hash,
- session->next_crypto->digest_len);
- ssh_string_burn(sig_blob);
- SSH_STRING_FREE(sig_blob);
- ssh_signature_free(sig);
- if (rc == SSH_ERROR) {
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,"Signature verified and valid");
+ if (session->server) {
+ /* server things are done in server.c */
+ session->dh_handshake_state=DH_STATE_FINISHED;
+ } else {
+ ssh_key server_key = NULL;
- /* When receiving this packet, we switch on the incomming crypto. */
- rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
- if (rc != SSH_OK) {
- goto error;
+ /* client */
+
+ /* Verify the host's signature. FIXME do it sooner */
+ sig_blob = session->next_crypto->dh_server_signature;
+ session->next_crypto->dh_server_signature = NULL;
+
+ /* get the server public key */
+ server_key = ssh_dh_get_next_server_publickey(session);
+ if (server_key == NULL) {
+ goto error;
+ }
+
+ rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
+ ssh_string_burn(sig_blob);
+ SSH_STRING_FREE(sig_blob);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ /* Check if signature from server matches user preferences */
+ if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
+ rc = ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
+ sig->type_c);
+ if (rc == 0) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Public key from server (%s) doesn't match user "
+ "preference (%s)",
+ sig->type_c,
+ session->opts.wanted_methods[SSH_HOSTKEYS]);
+ goto error;
+ }
+ }
+
+ rc = ssh_pki_signature_verify(session,
+ sig,
+ server_key,
+ session->next_crypto->secret_hash,
+ session->next_crypto->digest_len);
+ SSH_SIGNATURE_FREE(sig);
+ if (rc == SSH_ERROR) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to verify server hostkey signature");
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Signature verified and valid");
+
+ /* When receiving this packet, we switch on the incoming crypto. */
+ rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
- }
- session->dh_handshake_state = DH_STATE_FINISHED;
- session->ssh_connection_callback(session);
- return SSH_PACKET_USED;
+ session->dh_handshake_state = DH_STATE_FINISHED;
+ session->ssh_connection_callback(session);
+ return SSH_PACKET_USED;
+
error:
- session->session_state = SSH_SESSION_STATE_ERROR;
- return SSH_PACKET_USED;
+ SSH_SIGNATURE_FREE(sig);
+ ssh_string_burn(sig_blob);
+ SSH_STRING_FREE(sig_blob);
+ session->session_state = SSH_SESSION_STATE_ERROR;
+ return SSH_PACKET_USED;
}
/**
@@ -179,16 +220,16 @@ error:
* @brief handles a SSH_SERVICE_ACCEPT packet
*
*/
-SSH_PACKET_CALLBACK(ssh_packet_service_accept){
- (void)packet;
- (void)type;
- (void)user;
+SSH_PACKET_CALLBACK(ssh_packet_service_accept)
+{
+ (void)packet;
+ (void)type;
+ (void)user;
session->auth.service_state = SSH_AUTH_SERVICE_ACCEPTED;
- SSH_LOG(SSH_LOG_PACKET,
- "Received SSH_MSG_SERVICE_ACCEPT");
+ SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_SERVICE_ACCEPT");
- return SSH_PACKET_USED;
+ return SSH_PACKET_USED;
}
/**
@@ -201,6 +242,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
int rc;
uint32_t nr_extensions = 0;
uint32_t i;
+
(void)type;
(void)user;
@@ -218,7 +260,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
return SSH_PACKET_USED;
}
- SSH_LOG(SSH_LOG_PACKET, "Follows %u extensions", nr_extensions);
+ SSH_LOG(SSH_LOG_PACKET, "Follows %" PRIu32 " extensions", nr_extensions);
for (i = 0; i < nr_extensions; i++) {
char *name = NULL;
@@ -241,6 +283,8 @@ SSH_PACKET_CALLBACK(ssh_packet_ext_info)
if (ssh_match_group(value, "rsa-sha2-256")) {
session->extensions |= SSH_EXT_SIG_RSA_SHA256;
}
+ } else {
+ SSH_LOG(SSH_LOG_PACKET, "Unknown extension: %s", name);
}
free(name);
free(value);
diff --git a/src/packet_crypt.c b/src/packet_crypt.c
index c2f7ab02..fe3f489e 100644
--- a/src/packet_crypt.c
+++ b/src/packet_crypt.c
@@ -130,14 +130,15 @@ int ssh_packet_decrypt(ssh_session session,
return 0;
}
-unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
+unsigned char *ssh_packet_encrypt(ssh_session session, void *data, size_t len)
{
struct ssh_crypto_struct *crypto = NULL;
struct ssh_cipher_struct *cipher = NULL;
HMACCTX ctx = NULL;
char *out = NULL;
- int etm_packet_offset = 0;
- unsigned int finallen, blocksize;
+ int etm_packet_offset = 0, rc;
+ unsigned int blocksize;
+ size_t finallen = DIGEST_MAX_LEN;
uint32_t seq, lenfield_blocksize;
enum ssh_hmac_e type;
bool etm;
@@ -161,7 +162,7 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
if ((len - lenfield_blocksize - etm_packet_offset) % blocksize != 0) {
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set"
- " on at least one blocksize (received %d)", len);
+ " on at least one blocksize (received %zu)", len);
return NULL;
}
out = calloc(1, len);
@@ -185,9 +186,21 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
}
if (!etm) {
- hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
- hmac_update(ctx, data, len);
- hmac_final(ctx, crypto->hmacbuf, &finallen);
+ rc = hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_update(ctx, data, len);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_final(ctx, crypto->hmacbuf, &finallen);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
}
}
@@ -197,14 +210,26 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
if (type != SSH_HMAC_NONE) {
if (etm) {
PUSH_BE_U32(data, 0, len - etm_packet_offset);
- hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
- hmac_update(ctx, data, len);
- hmac_final(ctx, crypto->hmacbuf, &finallen);
+ rc = hmac_update(ctx, (unsigned char *)&seq, sizeof(uint32_t));
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_update(ctx, data, len);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
+ rc = hmac_final(ctx, crypto->hmacbuf, &finallen);
+ if (rc != 1) {
+ SAFE_FREE(out);
+ return NULL;
+ }
}
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("mac: ", data, len);
if (finallen != hmac_digest_len(type)) {
- printf("Final len is %d\n", finallen);
+ printf("Final len is %zu\n", finallen);
}
ssh_log_hexdump("Packet hmac", crypto->hmacbuf, hmac_digest_len(type));
#endif
@@ -216,17 +241,6 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
return crypto->hmacbuf;
}
-static int secure_memcmp(const void *s1, const void *s2, size_t n)
-{
- int rc = 0;
- const unsigned char *p1 = s1;
- const unsigned char *p2 = s2;
- for (; n > 0; --n) {
- rc |= *p1++ ^ *p2++;
- }
- return (rc != 0);
-}
-
/**
* @internal
*
@@ -246,42 +260,71 @@ int ssh_packet_hmac_verify(ssh_session session,
uint8_t *mac,
enum ssh_hmac_e type)
{
- struct ssh_crypto_struct *crypto = NULL;
- unsigned char hmacbuf[DIGEST_MAX_LEN] = {0};
- HMACCTX ctx;
- unsigned int hmaclen;
- uint32_t seq;
-
- /* AEAD types have no mac checking */
- if (type == SSH_HMAC_AEAD_POLY1305 ||
- type == SSH_HMAC_AEAD_GCM) {
- return SSH_OK;
- }
+ struct ssh_crypto_struct *crypto = NULL;
+ unsigned char hmacbuf[DIGEST_MAX_LEN] = {0};
+ HMACCTX ctx;
+ size_t hmaclen = DIGEST_MAX_LEN;
+ uint32_t seq;
+ int cmp;
+ int rc;
- crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
- if (crypto == NULL) {
- return SSH_ERROR;
- }
+ /* AEAD types have no mac checking */
+ if (type == SSH_HMAC_AEAD_POLY1305 ||
+ type == SSH_HMAC_AEAD_GCM) {
+ return SSH_OK;
+ }
- ctx = hmac_init(crypto->decryptMAC, hmac_digest_len(type), type);
- if (ctx == NULL) {
- return -1;
- }
+ crypto = ssh_packet_get_current_crypto(session,
+ SSH_DIRECTION_IN);
+ if (crypto == NULL) {
+ return SSH_ERROR;
+ }
- seq = htonl(session->recv_seq);
+ ctx = hmac_init(crypto->decryptMAC,
+ hmac_digest_len(type),
+ type);
+ if (ctx == NULL) {
+ return SSH_ERROR;
+ }
- hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t));
- hmac_update(ctx, data, len);
- hmac_final(ctx, hmacbuf, &hmaclen);
+ seq = htonl(session->recv_seq);
+
+ rc = hmac_update(ctx,
+ (unsigned char *) &seq,
+ sizeof(uint32_t));
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ rc = hmac_update(ctx,
+ data,
+ len);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
+ rc = hmac_final(ctx,
+ hmacbuf,
+ &hmaclen);
+ if (rc != 1) {
+ return SSH_ERROR;
+ }
#ifdef DEBUG_CRYPTO
- ssh_log_hexdump("received mac",mac,hmaclen);
- ssh_log_hexdump("Computed mac",hmacbuf,hmaclen);
- ssh_log_hexdump("seq",(unsigned char *)&seq,sizeof(uint32_t));
+ ssh_log_hexdump("received mac",
+ mac,
+ hmaclen);
+ ssh_log_hexdump("Computed mac",
+ hmacbuf,
+ hmaclen);
+ ssh_log_hexdump("seq",
+ (unsigned char *)&seq,
+ sizeof(uint32_t));
#endif
- if (secure_memcmp(mac, hmacbuf, hmaclen) == 0) {
- return 0;
- }
+ cmp = secure_memcmp(mac,
+ hmacbuf,
+ hmaclen);
+ if (cmp == 0) {
+ return SSH_OK;
+ }
- return -1;
+ return SSH_ERROR;
}
diff --git a/src/pcap.c b/src/pcap.c
index c089854a..3d295427 100644
--- a/src/pcap.c
+++ b/src/pcap.c
@@ -44,14 +44,11 @@
#include "libssh/socket.h"
/**
- * @internal
- *
* @defgroup libssh_pcap The libssh pcap functions
* @ingroup libssh
*
* The pcap file generation
*
- *
* @{
*/
@@ -63,7 +60,7 @@ struct pcap_hdr_s {
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
- int32_t thiszone; /* GMT to local correction */
+ int32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
@@ -98,12 +95,11 @@ struct pcaprec_hdr_s {
* in a SSH session only. Multiple pcap contexts may be used into
* a single pcap file.
*/
-
struct ssh_pcap_context_struct {
ssh_session session;
ssh_pcap_file file;
int connected;
- /* All of these information are useful to generate
+ /* All of this information is useful to generate
* the dummy IP and TCP packets
*/
uint32_t ipsource;
@@ -126,10 +122,11 @@ struct ssh_pcap_file_struct {
/**
* @brief create a new ssh_pcap_file object
*/
-ssh_pcap_file ssh_pcap_file_new(void) {
- struct ssh_pcap_file_struct *pcap;
+ssh_pcap_file ssh_pcap_file_new(void)
+{
+ struct ssh_pcap_file_struct *pcap = NULL;
- pcap = (struct ssh_pcap_file_struct *) malloc(sizeof(struct ssh_pcap_file_struct));
+ pcap = malloc(sizeof(struct ssh_pcap_file_struct));
if (pcap == NULL) {
return NULL;
}
@@ -141,163 +138,182 @@ ssh_pcap_file ssh_pcap_file_new(void) {
/** @internal
* @brief writes a packet on file
*/
-static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet){
- int err;
- uint32_t len;
- if(pcap == NULL || pcap->output==NULL)
- return SSH_ERROR;
- len=ssh_buffer_get_len(packet);
- err=fwrite(ssh_buffer_get(packet),len,1,pcap->output);
- if(err<0)
- return SSH_ERROR;
- else
- return SSH_OK;
+static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet)
+{
+ int err;
+ uint32_t len;
+ if (pcap == NULL || pcap->output == NULL) {
+ return SSH_ERROR;
+ }
+ len = ssh_buffer_get_len(packet);
+ err = fwrite(ssh_buffer_get(packet), len, 1, pcap->output);
+ if (err < 0) {
+ return SSH_ERROR;
+ } else {
+ return SSH_OK;
+ }
}
/** @internal
* @brief prepends a packet with the pcap header and writes packet
* on file
*/
-int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len){
- ssh_buffer header=ssh_buffer_new();
- struct timeval now;
- int err;
- if(header == NULL)
- return SSH_ERROR;
- gettimeofday(&now,NULL);
+int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len)
+{
+ ssh_buffer header = ssh_buffer_new();
+ struct timeval now;
+ int err;
+
+ if (header == NULL) {
+ return SSH_ERROR;
+ }
+
+ gettimeofday(&now, NULL);
err = ssh_buffer_allocate_size(header,
sizeof(uint32_t) * 4 +
ssh_buffer_get_len(packet));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(now.tv_sec));
+ err = ssh_buffer_add_u32(header, htonl(now.tv_sec));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(now.tv_usec));
+ err = ssh_buffer_add_u32(header, htonl(now.tv_usec));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(ssh_buffer_get_len(packet)));
+ err = ssh_buffer_add_u32(header, htonl(ssh_buffer_get_len(packet)));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(original_len));
+ err = ssh_buffer_add_u32(header, htonl(original_len));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_buffer(header,packet);
+ err = ssh_buffer_add_buffer(header, packet);
if (err < 0) {
goto error;
}
- err=ssh_pcap_file_write(pcap,header);
+ err = ssh_pcap_file_write(pcap, header);
error:
- SSH_BUFFER_FREE(header);
- return err;
+ SSH_BUFFER_FREE(header);
+ return err;
}
/**
- * @brief opens a new pcap file and create header
+ * @brief opens a new pcap file and creates header
*/
-int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){
- ssh_buffer header;
- int err;
- if(pcap == NULL)
- return SSH_ERROR;
- if(pcap->output){
- fclose(pcap->output);
- pcap->output=NULL;
- }
- pcap->output=fopen(filename,"wb");
- if(pcap->output==NULL)
- return SSH_ERROR;
- header=ssh_buffer_new();
- if(header==NULL)
- return SSH_ERROR;
+int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename)
+{
+ ssh_buffer header = NULL;
+ int err;
+
+ if (pcap == NULL) {
+ return SSH_ERROR;
+ }
+ if (pcap->output) {
+ fclose(pcap->output);
+ pcap->output = NULL;
+ }
+ pcap->output = fopen(filename, "wb");
+ if (pcap->output == NULL) {
+ return SSH_ERROR;
+ }
+ header = ssh_buffer_new();
+ if (header == NULL) {
+ return SSH_ERROR;
+ }
err = ssh_buffer_allocate_size(header,
sizeof(uint32_t) * 5 +
sizeof(uint16_t) * 2);
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u32(header,htonl(PCAP_MAGIC));
+ err = ssh_buffer_add_u32(header, htonl(PCAP_MAGIC));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u16(header,htons(PCAP_VERSION_MAJOR));
+ err = ssh_buffer_add_u16(header, htons(PCAP_VERSION_MAJOR));
if (err < 0) {
goto error;
}
- err = ssh_buffer_add_u16(header,htons(PCAP_VERSION_MINOR));
+ err = ssh_buffer_add_u16(header, htons(PCAP_VERSION_MINOR));
if (err < 0) {
goto error;
}
- /* currently hardcode GMT to 0 */
- err = ssh_buffer_add_u32(header,htonl(0));
+ /* currently hardcode GMT to 0 */
+ err = ssh_buffer_add_u32(header, htonl(0));
if (err < 0) {
goto error;
}
- /* accuracy */
- err = ssh_buffer_add_u32(header,htonl(0));
+ /* accuracy */
+ err = ssh_buffer_add_u32(header, htonl(0));
if (err < 0) {
goto error;
}
- /* size of the biggest packet */
- err = ssh_buffer_add_u32(header,htonl(MAX_PACKET_LEN));
+ /* size of the biggest packet */
+ err = ssh_buffer_add_u32(header, htonl(MAX_PACKET_LEN));
if (err < 0) {
goto error;
}
- /* we will write sort-of IP */
- err = ssh_buffer_add_u32(header,htonl(DLT_RAW));
+ /* we will write sort-of IP */
+ err = ssh_buffer_add_u32(header, htonl(DLT_RAW));
if (err < 0) {
goto error;
}
- err=ssh_pcap_file_write(pcap,header);
+ err = ssh_pcap_file_write(pcap,header);
error:
- SSH_BUFFER_FREE(header);
- return err;
+ SSH_BUFFER_FREE(header);
+ return err;
}
-int ssh_pcap_file_close(ssh_pcap_file pcap){
- int err;
- if(pcap ==NULL || pcap->output==NULL)
- return SSH_ERROR;
- err=fclose(pcap->output);
- pcap->output=NULL;
- if(err != 0)
- return SSH_ERROR;
- else
- return SSH_OK;
+int ssh_pcap_file_close(ssh_pcap_file pcap)
+{
+ int err;
+
+ if (pcap == NULL || pcap->output == NULL) {
+ return SSH_ERROR;
+ }
+ err = fclose(pcap->output);
+ pcap->output = NULL;
+ if (err != 0) {
+ return SSH_ERROR;
+ } else {
+ return SSH_OK;
+ }
}
-void ssh_pcap_file_free(ssh_pcap_file pcap){
- ssh_pcap_file_close(pcap);
- SAFE_FREE(pcap);
+void ssh_pcap_file_free(ssh_pcap_file pcap)
+{
+ ssh_pcap_file_close(pcap);
+ SAFE_FREE(pcap);
}
/** @internal
* @brief allocates a new ssh_pcap_context object
*/
-
-ssh_pcap_context ssh_pcap_context_new(ssh_session session){
- ssh_pcap_context ctx = (struct ssh_pcap_context_struct *) malloc(sizeof(struct ssh_pcap_context_struct));
- if(ctx==NULL){
- ssh_set_error_oom(session);
- return NULL;
- }
- ZERO_STRUCTP(ctx);
- ctx->session=session;
- return ctx;
+ssh_pcap_context ssh_pcap_context_new(ssh_session session)
+{
+ ssh_pcap_context ctx = (struct ssh_pcap_context_struct *)malloc(sizeof(struct ssh_pcap_context_struct));
+ if (ctx == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+ ZERO_STRUCTP(ctx);
+ ctx->session = session;
+ return ctx;
}
-void ssh_pcap_context_free(ssh_pcap_context ctx){
- SAFE_FREE(ctx);
+void ssh_pcap_context_free(ssh_pcap_context ctx)
+{
+ SAFE_FREE(ctx);
}
-void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap){
- ctx->file=pcap;
+void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap)
+{
+ ctx->file = pcap;
}
/** @internal
@@ -315,6 +331,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
socket_t fd;
socklen_t len;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (session == NULL) {
return SSH_ERROR;
@@ -337,7 +354,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
ssh_set_error(session,
SSH_REQUEST_DENIED,
"Getting local IP address: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -347,7 +364,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
ssh_set_error(session,
SSH_REQUEST_DENIED,
"Getting remote IP address: %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
@@ -383,7 +400,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx)
*/
int ssh_pcap_context_write(ssh_pcap_context ctx,
enum ssh_pcap_direction direction,
- void *data,
+ void *data,
uint32_t len,
uint32_t origlen)
{
@@ -417,7 +434,7 @@ int ssh_pcap_context_write(ssh_pcap_context ctx,
0); /* checksum */
ctx->file->ipsequence++;
- if (rc != SSH_OK){
+ if (rc != SSH_OK) {
goto error;
}
if (direction == SSH_PCAP_DIR_OUT) {
@@ -506,23 +523,25 @@ error:
/** @brief sets the pcap file used to trace the session
* @param current session
- * @param pcap an handler to a pcap file. A pcap file may be used in several
+ * @param pcap a handler to a pcap file. A pcap file may be used in several
* sessions.
* @returns SSH_ERROR in case of error, SSH_OK otherwise.
*/
-int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){
- ssh_pcap_context ctx=ssh_pcap_context_new(session);
- if(ctx==NULL){
- ssh_set_error_oom(session);
- return SSH_ERROR;
- }
- ctx->file=pcap;
- if(session->pcap_ctx)
- ssh_pcap_context_free(session->pcap_ctx);
- session->pcap_ctx=ctx;
- return SSH_OK;
+int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap)
+{
+ ssh_pcap_context ctx = ssh_pcap_context_new(session);
+ if (ctx == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+ ctx->file = pcap;
+ if (session->pcap_ctx) {
+ ssh_pcap_context_free(session->pcap_ctx);
+ }
+ session->pcap_ctx = ctx;
+ return SSH_OK;
}
-
+/** @} */
#else /* WITH_PCAP */
@@ -531,30 +550,36 @@ int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){
#include "libssh/libssh.h"
#include "libssh/priv.h"
-int ssh_pcap_file_close(ssh_pcap_file pcap){
- (void) pcap;
- return SSH_ERROR;
+int ssh_pcap_file_close(ssh_pcap_file pcap)
+{
+ (void)pcap;
+
+ return SSH_ERROR;
}
-void ssh_pcap_file_free(ssh_pcap_file pcap){
- (void) pcap;
+void ssh_pcap_file_free(ssh_pcap_file pcap)
+{
+ (void)pcap;
}
-ssh_pcap_file ssh_pcap_file_new(void){
- return NULL;
+ssh_pcap_file ssh_pcap_file_new(void)
+{
+ return NULL;
}
-int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){
- (void) pcap;
- (void) filename;
- return SSH_ERROR;
+int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename)
+{
+ (void)pcap;
+ (void)filename;
+
+ return SSH_ERROR;
}
-int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile){
- (void) pcapfile;
- ssh_set_error(session,SSH_REQUEST_DENIED,"Pcap support not compiled in");
- return SSH_ERROR;
+int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile)
+{
+ (void)pcapfile;
+
+ ssh_set_error(session, SSH_REQUEST_DENIED, "Pcap support not compiled in");
+ return SSH_ERROR;
}
#endif
-
-/** @} */
diff --git a/src/pki.c b/src/pki.c
index 0d86fbcd..816b7e6f 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -43,20 +43,6 @@
#include <sys/stat.h>
#include <sys/types.h>
-#ifdef _WIN32
-# ifdef HAVE_IO_H
-# include <io.h>
-# undef open
-# define open _open
-# undef close
-# define close _close
-# undef read
-# define read _read
-# undef unlink
-# define unlink _unlink
-# endif /* HAVE_IO_H */
-#endif
-
#include "libssh/libssh.h"
#include "libssh/session.h"
#include "libssh/priv.h"
@@ -67,17 +53,16 @@
#include "libssh/misc.h"
#include "libssh/agent.h"
+#ifndef MAX_LINE_SIZE
+#define MAX_LINE_SIZE 4096
+#endif /* NOT MAX_LINE_SIZE */
+
#define PKCS11_URI "pkcs11:"
enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey)
{
char *start = NULL;
- start = strstr(privkey, DSA_HEADER_BEGIN);
- if (start != NULL) {
- return SSH_KEYTYPE_DSS;
- }
-
start = strstr(privkey, RSA_HEADER_BEGIN);
if (start != NULL) {
return SSH_KEYTYPE_RSA;
@@ -113,22 +98,31 @@ const char *ssh_pki_key_ecdsa_name(const ssh_key key)
return pki_key_ecdsa_nid_to_name(key->ecdsa_nid);
#else
return NULL;
-#endif
+#endif /* HAVE_ECC */
}
/**
* @brief creates a new empty SSH key
+ *
* @returns an empty ssh_key handle, or NULL on error.
*/
-ssh_key ssh_key_new (void) {
- ssh_key ptr = malloc (sizeof (struct ssh_key_struct));
- if (ptr == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(ptr);
- return ptr;
+ssh_key ssh_key_new (void)
+{
+ ssh_key ptr = malloc (sizeof (struct ssh_key_struct));
+ if (ptr == NULL) {
+ return NULL;
+ }
+ ZERO_STRUCTP(ptr);
+ return ptr;
}
+/**
+ * @brief duplicates the key
+ *
+ * @param key An ssh_key to duplicate
+ *
+ * @return A duplicated ssh_key key
+ */
ssh_key ssh_key_dup(const ssh_key key)
{
if (key == NULL) {
@@ -142,39 +136,22 @@ ssh_key ssh_key_dup(const ssh_key key)
* @brief clean up the key and deallocate all existing keys
* @param[in] key ssh_key to clean
*/
-void ssh_key_clean (ssh_key key){
- if(key == NULL)
+void ssh_key_clean (ssh_key key)
+{
+ if (key == NULL)
return;
-#ifdef HAVE_LIBGCRYPT
- if(key->dsa) gcry_sexp_release(key->dsa);
- if(key->rsa) gcry_sexp_release(key->rsa);
- if(key->ecdsa) gcry_sexp_release(key->ecdsa);
-#elif defined HAVE_LIBCRYPTO
- if(key->dsa) DSA_free(key->dsa);
- if(key->rsa) RSA_free(key->rsa);
-#ifdef HAVE_OPENSSL_ECC
- if(key->ecdsa) EC_KEY_free(key->ecdsa);
-#endif /* HAVE_OPENSSL_ECC */
-#elif defined HAVE_LIBMBEDCRYPTO
- if (key->rsa != NULL) {
- mbedtls_pk_free(key->rsa);
- SAFE_FREE(key->rsa);
- }
- if (key->ecdsa != NULL) {
- mbedtls_ecdsa_free(key->ecdsa);
- SAFE_FREE(key->ecdsa);
- }
-#endif
+ pki_key_clean(key);
+
if (key->ed25519_privkey != NULL){
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* In OpenSSL implementation the private key is only the private
* original seed. In the internal implementation the private key is the
* concatenation of the original private seed with the public key.*/
explicit_bzero(key->ed25519_privkey, ED25519_KEY_LEN);
#else
explicit_bzero(key->ed25519_privkey, sizeof(ed25519_privkey));
-#endif
+#endif /* HAVE_LIBCRYPTO*/
SAFE_FREE(key->ed25519_privkey);
}
SAFE_FREE(key->ed25519_pubkey);
@@ -189,21 +166,19 @@ void ssh_key_clean (ssh_key key){
ssh_string_free(key->sk_application);
}
key->cert_type = SSH_KEYTYPE_UNKNOWN;
- key->flags=SSH_KEY_FLAG_EMPTY;
- key->type=SSH_KEYTYPE_UNKNOWN;
+ key->flags = SSH_KEY_FLAG_EMPTY;
+ key->type = SSH_KEYTYPE_UNKNOWN;
key->ecdsa_nid = 0;
- key->type_c=NULL;
- key->dsa = NULL;
- key->rsa = NULL;
- key->ecdsa = NULL;
+ key->type_c = NULL;
}
/**
* @brief deallocate a SSH key
* @param[in] key ssh_key handle to free
*/
-void ssh_key_free (ssh_key key){
- if(key){
+void ssh_key_free (ssh_key key)
+{
+ if (key) {
ssh_key_clean(key);
SAFE_FREE(key);
}
@@ -212,15 +187,16 @@ void ssh_key_free (ssh_key key){
/**
* @brief returns the type of a ssh key
* @param[in] key the ssh_key handle
- * @returns one of SSH_KEYTYPE_RSA, SSH_KEYTYPE_DSS,
+ * @returns one of SSH_KEYTYPE_RSA,
* SSH_KEYTYPE_ECDSA_P256, SSH_KEYTYPE_ECDSA_P384,
- * SSH_KEYTYPE_ECDSA_P521, SSH_KEYTYPE_ED25519, SSH_KEYTYPE_DSS_CERT01,
+ * SSH_KEYTYPE_ECDSA_P521, SSH_KEYTYPE_ED25519,
* SSH_KEYTYPE_RSA_CERT01, SSH_KEYTYPE_ECDSA_P256_CERT01,
* SSH_KEYTYPE_ECDSA_P384_CERT01, SSH_KEYTYPE_ECDSA_P521_CERT01, or
* SSH_KEYTYPE_ED25519_CERT01.
* @returns SSH_KEYTYPE_UNKNOWN if the type is unknown
*/
-enum ssh_keytypes_e ssh_key_type(const ssh_key key){
+enum ssh_keytypes_e ssh_key_type(const ssh_key key)
+{
if (key == NULL) {
return SSH_KEYTYPE_UNKNOWN;
}
@@ -232,6 +208,8 @@ enum ssh_keytypes_e ssh_key_type(const ssh_key key){
*
* @param[in] type The algorithm type to convert.
*
+ * @param[in] hash_type The hash type to convert
+ *
* @return A string for the keytype or NULL if unknown.
*/
const char *
@@ -282,8 +260,6 @@ ssh_key_signature_to_char(enum ssh_keytypes_e type,
*/
const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
switch (type) {
- case SSH_KEYTYPE_DSS:
- return "ssh-dss";
case SSH_KEYTYPE_RSA:
return "ssh-rsa";
case SSH_KEYTYPE_ECDSA:
@@ -296,8 +272,6 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
return "ecdsa-sha2-nistp521";
case SSH_KEYTYPE_ED25519:
return "ssh-ed25519";
- case SSH_KEYTYPE_DSS_CERT01:
- return "ssh-dss-cert-v01@openssh.com";
case SSH_KEYTYPE_RSA_CERT01:
return "ssh-rsa-cert-v01@openssh.com";
case SSH_KEYTYPE_ECDSA_P256_CERT01:
@@ -316,7 +290,9 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
return "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com";
case SSH_KEYTYPE_SK_ED25519_CERT01:
return "sk-ssh-ed25519-cert-v01@openssh.com";
+ case SSH_KEYTYPE_DSS: /* deprecated */
case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_UNKNOWN:
return NULL;
}
@@ -334,8 +310,6 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name)
if (strcmp(name, "ssh-rsa") == 0) {
return SSH_DIGEST_SHA1;
- } else if (strcmp(name, "ssh-dss") == 0) {
- return SSH_DIGEST_SHA1;
} else if (strcmp(name, "rsa-sha2-256") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "rsa-sha2-512") == 0) {
@@ -354,7 +328,7 @@ enum ssh_digest_e ssh_key_hash_from_name(const char *name)
return SSH_DIGEST_AUTO;
}
- SSH_LOG(SSH_LOG_WARN, "Unknown signature name %s", name);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature name %s", name);
/* TODO we should rather fail */
return SSH_DIGEST_AUTO;
@@ -386,13 +360,13 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
else if (session->server) {
allowed_list = session->opts.wanted_methods[SSH_HOSTKEYS];
if (allowed_list == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Session invalid: no host key available");
+ SSH_LOG(SSH_LOG_TRACE, "Session invalid: no host key available");
return 0;
}
}
-#endif
+#endif /* WITH_SERVER */
else {
- SSH_LOG(SSH_LOG_WARN, "Session invalid: not set as client nor server");
+ SSH_LOG(SSH_LOG_TRACE, "Session invalid: not set as client nor server");
return 0;
}
@@ -400,6 +374,42 @@ int ssh_key_algorithm_allowed(ssh_session session, const char *type)
return ssh_match_group(allowed_list, type);
}
+bool ssh_key_size_allowed_rsa(int min_size, ssh_key key)
+{
+ int key_size = ssh_key_size(key);
+
+ if (min_size < 768) {
+ if (ssh_fips_mode()) {
+ min_size = 2048;
+ } else {
+ min_size = 1024;
+ }
+ }
+ return (key_size >= min_size);
+}
+
+/**
+ * @brief Check the given key is acceptable in regards to the key size policy
+ * specified by the configuration
+ *
+ * @param[in] session The SSH session
+ * @param[in] key The SSH key
+ * @returns true if the key is allowed, false otherwise
+ */
+bool ssh_key_size_allowed(ssh_session session, ssh_key key)
+{
+ int min_size = 0;
+
+ switch (ssh_key_type(key)) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ min_size = session->opts.rsa_min_size;
+ return ssh_key_size_allowed_rsa(min_size, key);
+ default:
+ return true;
+ }
+}
+
/**
* @brief Convert a key type to a hash type. This is usually unambiguous
* for all the key types, unless the SHA2 extension (RFC 8332) is
@@ -415,9 +425,6 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
enum ssh_keytypes_e type)
{
switch (type) {
- case SSH_KEYTYPE_DSS_CERT01:
- case SSH_KEYTYPE_DSS:
- return SSH_DIGEST_SHA1;
case SSH_KEYTYPE_RSA_CERT01:
/* If we are talking to an old OpenSSH version which does not support
* SHA2 in certificates */
@@ -459,10 +466,12 @@ enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
case SSH_KEYTYPE_ED25519:
return SSH_DIGEST_AUTO;
case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Digest algorithm to be used with key type %u "
+ SSH_LOG(SSH_LOG_TRACE, "Digest algorithm to be used with key type %u "
"is not defined", type);
}
@@ -533,19 +542,16 @@ enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name) {
*
* @return The enum ssh key type.
*/
-enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
+enum ssh_keytypes_e ssh_key_type_from_name(const char *name)
+{
if (name == NULL) {
return SSH_KEYTYPE_UNKNOWN;
}
if (strcmp(name, "rsa") == 0) {
return SSH_KEYTYPE_RSA;
- } else if (strcmp(name, "dsa") == 0) {
- return SSH_KEYTYPE_DSS;
} else if (strcmp(name, "ssh-rsa") == 0) {
return SSH_KEYTYPE_RSA;
- } else if (strcmp(name, "ssh-dss") == 0) {
- return SSH_KEYTYPE_DSS;
} else if (strcmp(name, "ssh-ecdsa") == 0
|| strcmp(name, "ecdsa") == 0
|| strcmp(name, "ecdsa-sha2-nistp256") == 0) {
@@ -556,8 +562,6 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
return SSH_KEYTYPE_ECDSA_P521;
} else if (strcmp(name, "ssh-ed25519") == 0){
return SSH_KEYTYPE_ED25519;
- } else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) {
- return SSH_KEYTYPE_DSS_CERT01;
} else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
return SSH_KEYTYPE_RSA_CERT01;
} else if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) {
@@ -582,16 +586,15 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
}
/**
- * @brief Get the pubic key type corresponding to a certificate type.
+ * @brief Get the public key type corresponding to a certificate type.
*
* @param[in] type The certificate or public key type.
*
* @return The matching public key type.
*/
-enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type) {
+enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type)
+{
switch (type) {
- case SSH_KEYTYPE_DSS_CERT01:
- return SSH_KEYTYPE_DSS;
case SSH_KEYTYPE_RSA_CERT01:
return SSH_KEYTYPE_RSA;
case SSH_KEYTYPE_ECDSA_P256_CERT01:
@@ -618,7 +621,8 @@ enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type) {
*
* @return 1 if it is a public key, 0 if not.
*/
-int ssh_key_is_public(const ssh_key k) {
+int ssh_key_is_public(const ssh_key k)
+{
if (k == NULL) {
return 0;
}
@@ -660,8 +664,8 @@ int ssh_key_cmp(const ssh_key k1,
return 1;
}
- if (k1->type != k2->type) {
- SSH_LOG(SSH_LOG_WARN, "key types don't match!");
+ if (ssh_key_type_plain(k1->type) != ssh_key_type_plain(k2->type)) {
+ SSH_LOG(SSH_LOG_DEBUG, "key types don't match!");
return 1;
}
@@ -681,6 +685,22 @@ int ssh_key_cmp(const ssh_key k1,
}
}
+ if (what == SSH_KEY_CMP_CERTIFICATE) {
+ if (!is_cert_type(k1->type) ||
+ !is_cert_type(k2->type)) {
+ return 1;
+ }
+ if (k1->cert == NULL || k2->cert == NULL) {
+ return 1;
+ }
+ if (ssh_buffer_get_len(k1->cert) != ssh_buffer_get_len(k2->cert)) {
+ return 1;
+ }
+ return memcmp(ssh_buffer_get(k1->cert),
+ ssh_buffer_get(k2->cert),
+ ssh_buffer_get_len(k1->cert));
+ }
+
if (k1->type == SSH_KEYTYPE_ED25519 ||
k1->type == SSH_KEYTYPE_SK_ED25519) {
return pki_ed25519_key_cmp(k1, k2, what);
@@ -709,17 +729,12 @@ void ssh_signature_free(ssh_signature sig)
}
switch(sig->type) {
- case SSH_KEYTYPE_DSS:
-#ifdef HAVE_LIBGCRYPT
- gcry_sexp_release(sig->dsa_sig);
-#endif
- break;
case SSH_KEYTYPE_RSA:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(sig->rsa_sig);
#elif defined HAVE_LIBMBEDCRYPTO
SAFE_FREE(sig->rsa_sig);
-#endif
+#endif /* HAVE_LIBGCRYPT */
break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
@@ -730,16 +745,17 @@ void ssh_signature_free(ssh_signature sig)
#elif defined HAVE_LIBMBEDCRYPTO
bignum_safe_free(sig->ecdsa_sig.r);
bignum_safe_free(sig->ecdsa_sig.s);
-#endif
+#endif /* HAVE_GCRYPT_ECC */
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
-#ifndef HAVE_OPENSSL_ED25519
+#ifndef HAVE_LIBCRYPTO
/* When using OpenSSL, the signature is stored in sig->raw_sig */
SAFE_FREE(sig->ed25519_sig);
-#endif
+#endif /* HAVE_LIBCRYPTO */
break;
- case SSH_KEYTYPE_DSS_CERT01:
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -760,7 +776,7 @@ void ssh_signature_free(ssh_signature sig)
}
/**
- * @brief import a base64 formated key from a memory c-string
+ * @brief import a base64 formatted key from a memory c-string
*
* @param[in] b64_key The c-string holding the base64 encoded key
*
@@ -771,7 +787,7 @@ void ssh_signature_free(ssh_signature sig)
* @param[in] auth_data Private data passed to the auth function.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free()
*
* @return SSH_ERROR in case of error, SSH_OK otherwise.
*
@@ -794,7 +810,7 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_INFO,
+ SSH_LOG(SSH_LOG_DEBUG,
"Trying to decode privkey passphrase=%s",
passphrase ? "true" : "false");
@@ -820,9 +836,10 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
return SSH_OK;
}
+
+
/**
- * @brief Convert a private key to a pem base64 encoded key, or OpenSSH format for
- * keytype ssh-ed25519
+ * @brief Convert a private key to a base64 encoded key in given format
*
* @param[in] privkey The private key to export.
*
@@ -834,15 +851,21 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
* @param[in] auth_data Private data passed to the auth function.
*
* @param[out] b64_key A pointer to store the allocated base64 encoded key. You
- * need to free the buffer.
+ * need to free the buffer using ssh_string_from_char().
+ *
+ * @param[in] format The file format (OpenSSH, PEM, or default)
*
* @return SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_string_free_char()
*/
-int ssh_pki_export_privkey_base64(const ssh_key privkey,
- const char *passphrase,
- ssh_auth_callback auth_fn,
- void *auth_data,
- char **b64_key)
+int
+ssh_pki_export_privkey_base64_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ char **b64_key,
+ enum ssh_file_format_e format)
{
ssh_string blob = NULL;
char *b64 = NULL;
@@ -851,16 +874,32 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey,
return SSH_ERROR;
}
- if (privkey->type == SSH_KEYTYPE_ED25519){
- blob = ssh_pki_openssh_privkey_export(privkey,
- passphrase,
- auth_fn,
- auth_data);
- } else {
+ /*
+ * For historic reasons, the Ed25519 keys are exported in OpenSSH file
+ * format by default also when built with OpenSSL.
+ */
+#ifdef HAVE_LIBCRYPTO
+ if (format == SSH_FILE_FORMAT_DEFAULT &&
+ privkey->type != SSH_KEYTYPE_ED25519) {
+ format = SSH_FILE_FORMAT_PEM;
+ }
+#endif /* HAVE_LIBCRYPTO */
+
+ switch (format) {
+ case SSH_FILE_FORMAT_PEM:
blob = pki_private_key_to_pem(privkey,
passphrase,
auth_fn,
auth_data);
+ break;
+ case SSH_FILE_FORMAT_DEFAULT:
+ /* default except (OpenSSL && !ED25519) handled above */
+ case SSH_FILE_FORMAT_OPENSSH:
+ blob = ssh_pki_openssh_privkey_export(privkey,
+ passphrase,
+ auth_fn,
+ auth_data);
+ break;
}
if (blob == NULL) {
return SSH_ERROR;
@@ -877,6 +916,42 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey,
return SSH_OK;
}
+ /**
+ * @brief Convert a private key to a pem base64 encoded key, or OpenSSH format for
+ * keytype ssh-ed25519
+ *
+ * @param[in] privkey The private key to export.
+ *
+ * @param[in] passphrase The passphrase to use to encrypt the key with or
+ * NULL. An empty string means no passphrase.
+ *
+ * @param[in] auth_fn An auth function you may want to use or NULL.
+ *
+ * @param[in] auth_data Private data passed to the auth function.
+ *
+ * @param[out] b64_key A pointer to store the allocated base64 encoded key. You
+ * need to free the buffer using ssh_string_from_char().
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ *
+ * @see ssh_string_free_char()
+ */
+int ssh_pki_export_privkey_base64(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ char **b64_key)
+{
+ return ssh_pki_export_privkey_base64_format(privkey,
+ passphrase,
+ auth_fn,
+ auth_data,
+ b64_key,
+ SSH_FILE_FORMAT_DEFAULT);
+}
+
+
+
/**
* @brief Import a private key from a file or a PKCS #11 device.
*
@@ -891,7 +966,7 @@ int ssh_pki_export_privkey_base64(const ssh_key privkey,
* @param[in] auth_data Private data passed to the auth function.
*
* @param[out] pkey A pointer to store the allocated ssh_key. You need to
- * free the key.
+ * free the key using ssh_key_free().
*
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
* denied, SSH_ERROR otherwise.
@@ -908,6 +983,7 @@ int ssh_pki_import_privkey_file(const char *filename,
FILE *file;
off_t size;
int rc;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
if (pkey == NULL || filename == NULL || *filename == '\0') {
return SSH_ERROR;
@@ -918,24 +994,24 @@ int ssh_pki_import_privkey_file(const char *filename,
rc = pki_uri_import(filename, pkey, SSH_KEY_PRIVATE);
return rc;
}
-#endif
+#endif /* WITH_PKCS11_URI */
file = fopen(filename, "rb");
if (file == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Error opening %s: %s",
filename,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_EOF;
}
rc = fstat(fileno(file), &sb);
if (rc < 0) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Error getting stat of %s: %s",
filename,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
switch (errno) {
case ENOENT:
case EACCES:
@@ -946,7 +1022,7 @@ int ssh_pki_import_privkey_file(const char *filename,
}
if (sb.st_size > MAX_PRIVKEY_SIZE) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Private key is bigger than 4M.");
fclose(file);
return SSH_ERROR;
@@ -955,7 +1031,7 @@ int ssh_pki_import_privkey_file(const char *filename,
key_buf = malloc(sb.st_size + 1);
if (key_buf == NULL) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
return SSH_ERROR;
}
@@ -964,10 +1040,10 @@ int ssh_pki_import_privkey_file(const char *filename,
if (size != sb.st_size) {
SAFE_FREE(key_buf);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Error reading %s: %s",
filename,
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
key_buf[size] = 0;
@@ -983,8 +1059,7 @@ int ssh_pki_import_privkey_file(const char *filename,
}
/**
- * @brief Export a private key to a pem file on disk, or OpenSSH format for
- * keytype ssh-ed25519
+ * @brief Export a private key to a file in format specified in the argument
*
* @param[in] privkey The private key to export.
*
@@ -997,16 +1072,21 @@ int ssh_pki_import_privkey_file(const char *filename,
*
* @param[in] filename The path where to store the pem file.
*
+ * @param[in] format The file format (OpenSSH, PEM, or default)
+ *
* @return SSH_OK on success, SSH_ERROR on error.
*/
-int ssh_pki_export_privkey_file(const ssh_key privkey,
- const char *passphrase,
- ssh_auth_callback auth_fn,
- void *auth_data,
- const char *filename)
+
+int
+ssh_pki_export_privkey_file_format(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename,
+ enum ssh_file_format_e format)
{
- ssh_string blob;
- FILE *fp;
+ ssh_string blob = NULL;
+ FILE *fp = NULL;
int rc;
if (privkey == NULL || !ssh_key_is_private(privkey)) {
@@ -1015,21 +1095,38 @@ int ssh_pki_export_privkey_file(const ssh_key privkey,
fp = fopen(filename, "wb");
if (fp == NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
SSH_LOG(SSH_LOG_FUNCTIONS, "Error opening %s: %s",
- filename, strerror(errno));
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_EOF;
}
- if (privkey->type == SSH_KEYTYPE_ED25519){
- blob = ssh_pki_openssh_privkey_export(privkey,
- passphrase,
- auth_fn,
- auth_data);
- } else {
+ /*
+ * For historic reasons, the Ed25519 keys are exported in OpenSSH file
+ * format by default also when built with OpenSSL.
+ */
+#ifdef HAVE_LIBCRYPTO
+ if (format == SSH_FILE_FORMAT_DEFAULT &&
+ privkey->type != SSH_KEYTYPE_ED25519) {
+ format = SSH_FILE_FORMAT_PEM;
+ }
+#endif /* HAVE_LIBCRYPTO */
+
+ switch (format) {
+ case SSH_FILE_FORMAT_PEM:
blob = pki_private_key_to_pem(privkey,
passphrase,
auth_fn,
auth_data);
+ break;
+ case SSH_FILE_FORMAT_DEFAULT:
+ /* default except (OpenSSL && !ED25519) handled above */
+ case SSH_FILE_FORMAT_OPENSSH:
+ blob = ssh_pki_openssh_privkey_export(privkey,
+ passphrase,
+ auth_fn,
+ auth_data);
+ break;
}
if (blob == NULL) {
fclose(fp);
@@ -1048,12 +1145,45 @@ int ssh_pki_export_privkey_file(const ssh_key privkey,
return SSH_OK;
}
-/* temporary function to migrate seemlessly to ssh_key */
-ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) {
+/**
+ * @brief Export a private key to a pem file on disk, or OpenSSH format for
+ * keytype ssh-ed25519
+ *
+ * @param[in] privkey The private key to export.
+ *
+ * @param[in] passphrase The passphrase to use to encrypt the key with or
+ * NULL. An empty string means no passphrase.
+ *
+ * @param[in] auth_fn An auth function you may want to use or NULL.
+ *
+ * @param[in] auth_data Private data passed to the auth function.
+ *
+ * @param[in] filename The path where to store the pem file.
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+int
+ssh_pki_export_privkey_file(const ssh_key privkey,
+ const char *passphrase,
+ ssh_auth_callback auth_fn,
+ void *auth_data,
+ const char *filename)
+{
+ return ssh_pki_export_privkey_file_format(privkey,
+ passphrase,
+ auth_fn,
+ auth_data,
+ filename,
+ SSH_FILE_FORMAT_DEFAULT);
+}
+
+/* temporary function to migrate seamlessly to ssh_key */
+ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key)
+{
ssh_public_key pub;
ssh_key tmp;
- if(key == NULL) {
+ if (key == NULL) {
return NULL;
}
@@ -1062,38 +1192,44 @@ ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key) {
return NULL;
}
- pub = malloc(sizeof(struct ssh_public_key_struct));
+ pub = calloc(1, sizeof(struct ssh_public_key_struct));
if (pub == NULL) {
ssh_key_free(tmp);
return NULL;
}
- ZERO_STRUCTP(pub);
pub->type = tmp->type;
pub->type_c = tmp->type_c;
- pub->dsa_pub = tmp->dsa;
- tmp->dsa = NULL;
+#ifndef HAVE_LIBCRYPTO
pub->rsa_pub = tmp->rsa;
tmp->rsa = NULL;
+#else
+ pub->key_pub = tmp->key;
+ tmp->key = NULL;
+#endif /* HAVE_LIBCRYPTO */
ssh_key_free(tmp);
return pub;
}
-ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key) {
+ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key)
+{
ssh_private_key privkey;
- privkey = malloc(sizeof(struct ssh_private_key_struct));
+ privkey = calloc(1, sizeof(struct ssh_private_key_struct));
if (privkey == NULL) {
ssh_key_free(key);
return NULL;
}
privkey->type = key->type;
- privkey->dsa_priv = key->dsa;
+#ifndef HAVE_LIBCRYPTO
privkey->rsa_priv = key->rsa;
+#else
+ privkey->key_priv = key->key;
+#endif /* HAVE_LIBCRYPTO */
return privkey;
}
@@ -1115,46 +1251,6 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
switch (type) {
- case SSH_KEYTYPE_DSS:
- {
- ssh_string p = NULL;
- ssh_string q = NULL;
- ssh_string g = NULL;
- ssh_string pubkey = NULL;
- ssh_string privkey = NULL;
-
- rc = ssh_buffer_unpack(buffer, "SSSSS", &p, &q, &g,
- &pubkey, &privkey);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
- goto fail;
- }
-
- rc = pki_privkey_build_dss(key, p, q, g, pubkey, privkey);
-#ifdef DEBUG_CRYPTO
- ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p));
- ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q));
- ssh_log_hexdump("g", ssh_string_data(g), ssh_string_len(g));
- ssh_log_hexdump("pubkey", ssh_string_data(pubkey),
- ssh_string_len(pubkey));
- ssh_log_hexdump("privkey", ssh_string_data(privkey),
- ssh_string_len(privkey));
-#endif
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(pubkey);
- SSH_STRING_FREE(pubkey);
- ssh_string_burn(privkey);
- SSH_STRING_FREE(privkey);
- if (rc == SSH_ERROR) {
- goto fail;
- }
- }
- break;
case SSH_KEYTYPE_RSA:
{
ssh_string n = NULL;
@@ -1167,7 +1263,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
rc = ssh_buffer_unpack(buffer, "SSSSSS", &n, &e, &d,
&iqmp, &p, &q);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1176,11 +1272,12 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
ssh_log_hexdump("n", ssh_string_data(n), ssh_string_len(n));
ssh_log_hexdump("e", ssh_string_data(e), ssh_string_len(e));
ssh_log_hexdump("d", ssh_string_data(d), ssh_string_len(d));
- ssh_log_hexdump("iqmp", ssh_string_data(iqmp),
- ssh_string_len(iqmp));
+ ssh_log_hexdump("iqmp",
+ ssh_string_data(iqmp),
+ ssh_string_len(iqmp));
ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p));
ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q));
-#endif
+#endif /* DEBUG_CRYPTO */
ssh_string_burn(n);
SSH_STRING_FREE(n);
ssh_string_burn(e);
@@ -1194,7 +1291,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
ssh_string_burn(q);
SSH_STRING_FREE(q);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build RSA private key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build RSA private key");
goto fail;
}
}
@@ -1211,7 +1308,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
rc = ssh_buffer_unpack(buffer, "SSS", &i, &e, &exp);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1231,19 +1328,19 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
ssh_string_burn(exp);
SSH_STRING_FREE(exp);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build ECDSA private key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build ECDSA private key");
goto fail;
}
}
break;
-#endif
+#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
{
ssh_string pubkey = NULL, privkey = NULL;
rc = ssh_buffer_unpack(buffer, "SS", &pubkey, &privkey);
if (rc != SSH_OK){
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1252,12 +1349,11 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
SSH_STRING_FREE(privkey);
SSH_STRING_FREE(pubkey);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build ed25519 key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build ed25519 key");
goto fail;
}
}
break;
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -1270,7 +1366,7 @@ int pki_import_privkey_buffer(enum ssh_keytypes_e type,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown private key type (%d)", type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown private key type (%d)", type);
goto fail;
}
@@ -1284,7 +1380,8 @@ fail:
static int pki_import_pubkey_buffer(ssh_buffer buffer,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_key key = NULL;
int rc;
@@ -1298,39 +1395,6 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
key->flags = SSH_KEY_FLAG_PUBLIC;
switch (type) {
- case SSH_KEYTYPE_DSS:
- {
- ssh_string p = NULL;
- ssh_string q = NULL;
- ssh_string g = NULL;
- ssh_string pubkey = NULL;
-
- rc = ssh_buffer_unpack(buffer, "SSSS", &p, &q, &g, &pubkey);
- if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
- goto fail;
- }
-
- rc = pki_pubkey_build_dss(key, p, q, g, pubkey);
-#ifdef DEBUG_CRYPTO
- ssh_log_hexdump("p", ssh_string_data(p), ssh_string_len(p));
- ssh_log_hexdump("q", ssh_string_data(q), ssh_string_len(q));
- ssh_log_hexdump("g", ssh_string_data(g), ssh_string_len(g));
-#endif
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(pubkey);
- SSH_STRING_FREE(pubkey);
- if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build DSA public key");
- goto fail;
- }
- }
- break;
case SSH_KEYTYPE_RSA:
{
ssh_string e = NULL;
@@ -1338,7 +1402,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
rc = ssh_buffer_unpack(buffer, "SS", &e, &n);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1346,13 +1410,13 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("e", ssh_string_data(e), ssh_string_len(e));
ssh_log_hexdump("n", ssh_string_data(n), ssh_string_len(n));
-#endif
+#endif /* DEBUG_CRYPTO */
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(n);
SSH_STRING_FREE(n);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build RSA public key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build RSA public key");
goto fail;
}
}
@@ -1370,7 +1434,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
rc = ssh_buffer_unpack(buffer, "SS", &i, &e);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
goto fail;
}
@@ -1386,7 +1450,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
ssh_string_burn(e);
SSH_STRING_FREE(e);
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to build ECDSA public key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to build ECDSA public key");
goto fail;
}
@@ -1399,7 +1463,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
if (type == SSH_KEYTYPE_SK_ECDSA) {
ssh_string application = ssh_buffer_get_ssh_string(buffer);
if (application == NULL) {
- SSH_LOG(SSH_LOG_WARN, "SK Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "SK Unpack error");
goto fail;
}
key->sk_application = application;
@@ -1407,14 +1471,14 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
}
}
break;
-#endif
+#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
{
ssh_string pubkey = ssh_buffer_get_ssh_string(buffer);
if (ssh_string_len(pubkey) != ED25519_KEY_LEN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid public key length");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid public key length");
ssh_string_burn(pubkey);
SSH_STRING_FREE(pubkey);
goto fail;
@@ -1434,14 +1498,13 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
if (type == SSH_KEYTYPE_SK_ED25519) {
ssh_string application = ssh_buffer_get_ssh_string(buffer);
if (application == NULL) {
- SSH_LOG(SSH_LOG_WARN, "SK Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "SK Unpack error");
goto fail;
}
key->sk_application = application;
}
}
break;
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -1452,7 +1515,7 @@ static int pki_import_pubkey_buffer(ssh_buffer buffer,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown public key protocol %d", type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown public key protocol %d", type);
goto fail;
}
@@ -1466,7 +1529,8 @@ fail:
static int pki_import_cert_buffer(ssh_buffer buffer,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_buffer cert;
ssh_string tmp_s;
const char *type_c;
@@ -1508,9 +1572,6 @@ static int pki_import_cert_buffer(ssh_buffer buffer,
SSH_STRING_FREE(tmp_s);
switch (type) {
- case SSH_KEYTYPE_DSS_CERT01:
- rc = pki_import_pubkey_buffer(buffer, SSH_KEYTYPE_DSS, &key);
- break;
case SSH_KEYTYPE_RSA_CERT01:
rc = pki_import_pubkey_buffer(buffer, SSH_KEYTYPE_RSA, &key);
break;
@@ -1541,7 +1602,7 @@ static int pki_import_cert_buffer(ssh_buffer buffer,
key->type = type;
key->type_c = type_c;
- key->cert = (void*) cert;
+ key->cert = cert;
*pkey = key;
return SSH_OK;
@@ -1553,14 +1614,14 @@ fail:
}
/**
- * @brief Import a base64 formated public key from a memory c-string.
+ * @brief Import a base64 formatted public key from a memory c-string.
*
* @param[in] b64_key The base64 key to format.
*
* @param[in] type The type of the key to format.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -1568,7 +1629,8 @@ fail:
*/
int ssh_pki_import_pubkey_base64(const char *b64_key,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_buffer buffer = NULL;
ssh_string type_s = NULL;
int rc;
@@ -1608,14 +1670,15 @@ int ssh_pki_import_pubkey_base64(const char *b64_key,
* 6.6 "Public Key Algorithms".
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_key_free()
*/
int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
ssh_buffer buffer = NULL;
ssh_string type_s = NULL;
enum ssh_keytypes_e type;
@@ -1627,26 +1690,26 @@ int ssh_pki_import_pubkey_blob(const ssh_string key_blob,
buffer = ssh_buffer_new();
if (buffer == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
return SSH_ERROR;
}
rc = ssh_buffer_add_data(buffer, ssh_string_data(key_blob),
ssh_string_len(key_blob));
if (rc < 0) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
goto fail;
}
type_s = ssh_buffer_get_ssh_string(buffer);
if (type_s == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
goto fail;
}
type = ssh_key_type_from_name(ssh_string_get_char(type_s));
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown key type found!");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown key type found!");
goto fail;
}
SSH_STRING_FREE(type_s);
@@ -1667,6 +1730,7 @@ fail:
return SSH_ERROR;
}
+#ifdef WITH_PKCS11_URI
/**
*@brief Detect if the pathname in cmp is a PKCS #11 URI.
*
@@ -1695,7 +1759,10 @@ bool ssh_pki_is_uri(const char *cmp)
*
* @param[in] priv_uri Private PKCS #11 URI.
*
- * @returns pointer to the public PKCS #11 URI
+ * @returns pointer to the public PKCS #11 URI. You need to free
+ * the memory using ssh_string_free_char().
+ *
+ * @see ssh_string_free_char().
*/
char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri)
{
@@ -1707,6 +1774,7 @@ char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri)
return pub_uri_temp;
}
+#endif /* WITH_PKCS11_URI */
/**
* @brief Import a public key from a file or a PKCS #11 device.
@@ -1715,7 +1783,7 @@ char *ssh_pki_export_pub_uri_from_priv_uri(const char *priv_uri)
* PKCS #11 URI corresponding to the public key.
*
* @param[out] pkey A pointer to store the allocated public key. You need to
- * free the memory.
+ * free the memory using ssh_key_free().
*
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
* denied, SSH_ERROR otherwise.
@@ -1726,12 +1794,14 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
{
enum ssh_keytypes_e type;
struct stat sb;
- char *key_buf, *p;
+ char *key_buf = NULL, *p = NULL;
size_t buflen, i;
- const char *q;
- FILE *file;
+ const char *q = NULL;
+ FILE *file = NULL;
off_t size;
int rc, cmp;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_key priv_key = NULL;
if (pkey == NULL || filename == NULL || *filename == '\0') {
return SSH_ERROR;
@@ -1742,20 +1812,20 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
rc = pki_uri_import(filename, pkey, SSH_KEY_PUBLIC);
return rc;
}
-#endif
+#endif /* WITH_PKCS11_URI */
file = fopen(filename, "rb");
if (file == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Error opening %s: %s",
- filename, strerror(errno));
+ SSH_LOG(SSH_LOG_TRACE, "Error opening %s: %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_EOF;
}
rc = fstat(fileno(file), &sb);
if (rc < 0) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN, "Error gettint stat of %s: %s",
- filename, strerror(errno));
+ SSH_LOG(SSH_LOG_TRACE, "Error gettint stat of %s: %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
switch (errno) {
case ENOENT:
case EACCES:
@@ -1772,7 +1842,7 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
key_buf = malloc(sb.st_size + 1);
if (key_buf == NULL) {
fclose(file);
- SSH_LOG(SSH_LOG_WARN, "Out of memory!");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory!");
return SSH_ERROR;
}
@@ -1781,8 +1851,8 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
if (size != sb.st_size) {
SAFE_FREE(key_buf);
- SSH_LOG(SSH_LOG_WARN, "Error reading %s: %s",
- filename, strerror(errno));
+ SSH_LOG(SSH_LOG_TRACE, "Error reading %s: %s",
+ filename, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
key_buf[size] = '\0';
@@ -1794,7 +1864,24 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
*pkey = ssh_pki_openssh_pubkey_import(key_buf);
SAFE_FREE(key_buf);
if (*pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Failed to import public key from OpenSSH"
+ SSH_LOG(SSH_LOG_TRACE, "Failed to import public key from OpenSSH"
+ " private key file");
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+ }
+
+ /*
+ * Try to parse key as PEM. Set empty passphrase, so user won't be prompted
+ * for passphrase. Don't try to decrypt encrypted private key.
+ */
+ priv_key = pki_private_key_from_base64(key_buf, "", NULL, NULL);
+ if (priv_key) {
+ rc = ssh_pki_export_privkey_to_pubkey(priv_key, pkey);
+ ssh_key_free(priv_key);
+ SAFE_FREE(key_buf);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_WARN, "Failed to import public key from PEM"
" private key file");
return SSH_ERROR;
}
@@ -1816,6 +1903,10 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
return SSH_ERROR;
}
+ if (i >= buflen) {
+ SAFE_FREE(key_buf);
+ return SSH_ERROR;
+ }
q = &p[i + 1];
for (; i < buflen; i++) {
if (isspace((int)p[i])) {
@@ -1831,14 +1922,14 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
}
/**
- * @brief Import a base64 formated certificate from a memory c-string.
+ * @brief Import a base64 formatted certificate from a memory c-string.
*
* @param[in] b64_cert The base64 cert to format.
*
* @param[in] type The type of the cert to format.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -1846,7 +1937,8 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
*/
int ssh_pki_import_cert_base64(const char *b64_cert,
enum ssh_keytypes_e type,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
return ssh_pki_import_pubkey_base64(b64_cert, type, pkey);
}
@@ -1859,14 +1951,15 @@ int ssh_pki_import_cert_base64(const char *b64_cert,
* 6.6 "Public Key Algorithms".
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_key_free()
*/
int ssh_pki_import_cert_blob(const ssh_string cert_blob,
- ssh_key *pkey) {
+ ssh_key *pkey)
+{
return ssh_pki_import_pubkey_blob(cert_blob, pkey);
}
@@ -1876,7 +1969,7 @@ int ssh_pki_import_cert_blob(const ssh_string cert_blob,
* @param[in] filename The path to the certificate.
*
* @param[out] pkey A pointer to store the allocated certificate. You need to
- * free the memory.
+ * free the memory using ssh_key_free().
*
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
* denied, SSH_ERROR otherwise.
@@ -1885,26 +1978,39 @@ int ssh_pki_import_cert_blob(const ssh_string cert_blob,
*/
int ssh_pki_import_cert_file(const char *filename, ssh_key *pkey)
{
- return ssh_pki_import_pubkey_file(filename, pkey);
+ int rc;
+
+ rc = ssh_pki_import_pubkey_file(filename, pkey);
+ if (rc == SSH_OK) {
+ /* check the key is a cert type. */
+ if (!is_cert_type((*pkey)->type)) {
+ SSH_KEY_FREE(*pkey);
+ return SSH_ERROR;
+ }
+ }
+
+ return rc;
}
/**
- * @brief Generates a keypair.
+ * @brief Generates a key pair.
*
* @param[in] type Type of key to create
*
* @param[in] parameter Parameter to the creation of key:
* rsa : length of the key in bits (e.g. 1024, 2048, 4096)
- * dsa : length of the key in bits (e.g. 1024, 2048, 3072)
* @param[out] pkey A pointer to store the allocated private key. You need
- * to free the memory.
+ * to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
* @warning Generating a key pair may take some time.
+ *
+ * @see ssh_key_free()
*/
int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
- ssh_key *pkey){
+ ssh_key *pkey)
+{
int rc;
ssh_key key = ssh_key_new();
@@ -1922,11 +2028,6 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
if(rc == SSH_ERROR)
goto error;
break;
- case SSH_KEYTYPE_DSS:
- rc = pki_key_generate_dss(key, parameter);
- if(rc == SSH_ERROR)
- goto error;
- break;
#ifdef HAVE_ECC
case SSH_KEYTYPE_ECDSA: /* deprecated */
rc = pki_key_generate_ecdsa(key, parameter);
@@ -1955,14 +2056,13 @@ int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
goto error;
}
break;
-#endif
+#endif /* HAVE_ECC */
case SSH_KEYTYPE_ED25519:
rc = pki_key_generate_ed25519(key);
if (rc == SSH_ERROR) {
goto error;
}
break;
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -1991,7 +2091,7 @@ error:
* @param[in] privkey The private key to get the public key from.
*
* @param[out] pkey A pointer to store the newly allocated public key. You
- * NEED to free the key.
+ * NEED to free the key using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -2029,11 +2129,11 @@ int ssh_pki_export_privkey_to_pubkey(const ssh_key privkey,
* from.
*
* @param[out] pblob A pointer to store the newly allocated key blob. You
- * NEED to free it.
+ * need to free it using ssh_string_free().
*
* @return SSH_OK on success, SSH_ERROR otherwise.
*
- * @see SSH_STRING_FREE()
+ * @see ssh_string_free()
*/
int ssh_pki_export_pubkey_blob(const ssh_key key,
ssh_string *pblob)
@@ -2044,7 +2144,42 @@ int ssh_pki_export_pubkey_blob(const ssh_key key,
return SSH_OK;
}
- blob = pki_publickey_to_blob(key);
+ blob = pki_key_to_blob(key, SSH_KEY_PUBLIC);
+ if (blob == NULL) {
+ return SSH_ERROR;
+ }
+
+ *pblob = blob;
+ return SSH_OK;
+}
+
+/**
+ * @internal
+ *
+ * @brief Create a key_blob from a private key.
+ *
+ * The "key_blob" is encoded as per draft-miller-ssh-agent-08 section 4.2
+ * "Adding keys to the agent" for any of the supported key types.
+ *
+ * @param[in] key A private key to create the private ssh_string from.
+ *
+ * @param[out] pblob A pointer to store the newly allocated key blob. You
+ * need to free it using ssh_string_free().
+ *
+ * @return SSH_OK on success, SSH_ERROR otherwise.
+ *
+ * @see ssh_string_free()
+ */
+int ssh_pki_export_privkey_blob(const ssh_key key,
+ ssh_string *pblob)
+{
+ ssh_string blob;
+
+ if (key == NULL) {
+ return SSH_OK;
+ }
+
+ blob = pki_key_to_blob(key, SSH_KEY_PRIVATE);
if (blob == NULL) {
return SSH_ERROR;
}
@@ -2059,11 +2194,11 @@ int ssh_pki_export_pubkey_blob(const ssh_key key,
* @param[in] key The key to hash
*
* @param[out] b64_key A pointer to store the allocated base64 encoded key. You
- * need to free the buffer.
+ * need to free the buffer using ssh_string_free_char()
*
* @return SSH_OK on success, SSH_ERROR on error.
*
- * @see SSH_STRING_FREE_CHAR()
+ * @see ssh_string_free_char()
*/
int ssh_pki_export_pubkey_base64(const ssh_key key,
char **b64_key)
@@ -2075,7 +2210,7 @@ int ssh_pki_export_pubkey_base64(const ssh_key key,
return SSH_ERROR;
}
- key_blob = pki_publickey_to_blob(key);
+ key_blob = pki_key_to_blob(key, SSH_KEY_PUBLIC);
if (key_blob == NULL) {
return SSH_ERROR;
}
@@ -2091,10 +2226,22 @@ int ssh_pki_export_pubkey_base64(const ssh_key key,
return SSH_OK;
}
+/**
+ * @brief Export public key to file
+ *
+ * Exports the public key in AuthorizedKeysFile acceptable format.
+ * For more information see `man sshd`
+ *
+ * @param key A key to export
+ *
+ * @param filename The name of the output file
+ *
+ * @returns SSH_OK on success, SSH_ERROR otherwise.
+ */
int ssh_pki_export_pubkey_file(const ssh_key key,
const char *filename)
{
- char key_buf[4096];
+ char key_buf[MAX_LINE_SIZE];
char host[256];
char *b64_key;
char *user;
@@ -2160,7 +2307,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
**/
int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) {
ssh_buffer cert_buffer;
- int rc;
+ int rc, cmp;
if (certkey == NULL || privkey == NULL) {
return SSH_ERROR;
@@ -2174,6 +2321,12 @@ int ssh_pki_copy_cert_to_privkey(const ssh_key certkey, ssh_key privkey) {
return SSH_ERROR;
}
+ /* make sure the public keys match */
+ cmp = ssh_key_cmp(certkey, privkey, SSH_KEY_CMP_PUBLIC);
+ if (cmp != 0) {
+ return SSH_ERROR;
+ }
+
cert_buffer = ssh_buffer_new();
if (cert_buffer == NULL) {
return SSH_ERROR;
@@ -2238,8 +2391,12 @@ int ssh_pki_export_signature_blob(const ssh_signature sig,
return SSH_ERROR;
}
- ssh_string_fill(str, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
+ rc = ssh_string_fill(str, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
SSH_BUFFER_FREE(buf);
+ if (rc < 0) {
+ SSH_STRING_FREE(str);
+ return SSH_ERROR;
+ }
*sig_blob = str;
@@ -2340,22 +2497,11 @@ int pki_key_check_hash_compatible(ssh_key key,
}
switch(key->type) {
- case SSH_KEYTYPE_DSS_CERT01:
- case SSH_KEYTYPE_DSS:
- if (hash_type == SSH_DIGEST_SHA1) {
- if (ssh_fips_mode()) {
- SSH_LOG(SSH_LOG_WARN, "SHA1 is not allowed in FIPS mode");
- return SSH_ERROR;
- } else {
- return SSH_OK;
- }
- }
- break;
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_RSA:
if (hash_type == SSH_DIGEST_SHA1) {
if (ssh_fips_mode()) {
- SSH_LOG(SSH_LOG_WARN, "SHA1 is not allowed in FIPS mode");
+ SSH_LOG(SSH_LOG_TRACE, "SHA1 is not allowed in FIPS mode");
return SSH_ERROR;
} else {
return SSH_OK;
@@ -2396,14 +2542,16 @@ int pki_key_check_hash_compatible(ssh_key key,
return SSH_OK;
}
break;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_ECDSA:
case SSH_KEYTYPE_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown key type %d", key->type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown key type %d", key->type);
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_WARN, "Key type %d incompatible with hash type %d",
+ SSH_LOG(SSH_LOG_TRACE, "Key type %d incompatible with hash type %d",
key->type, hash_type);
return SSH_ERROR;
@@ -2416,6 +2564,7 @@ int ssh_pki_signature_verify(ssh_session session,
size_t input_len)
{
int rc;
+ bool allowed;
enum ssh_keytypes_e key_type;
if (session == NULL || sig == NULL || key == NULL || input == NULL) {
@@ -2430,12 +2579,19 @@ int ssh_pki_signature_verify(ssh_session session,
sig->type_c);
if (key_type != sig->type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Can not verify %s signature with %s key",
sig->type_c, key->type_c);
return SSH_ERROR;
}
+ allowed = ssh_key_size_allowed(session, key);
+ if (!allowed) {
+ ssh_set_error(session, SSH_FATAL, "The '%s' key of size %d is not "
+ "allowed by RSA_MIN_SIZE", key->type_c, ssh_key_size(key));
+ return SSH_ERROR;
+ }
+
/* Check if public key and hash type are compatible */
rc = pki_key_check_hash_compatible(key, sig->hash_type);
if (rc != SSH_OK) {
@@ -2454,7 +2610,7 @@ int ssh_pki_signature_verify(ssh_session session,
ctx = sha256_init();
if (ctx == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Can not create SHA256CTX for application hash");
return SSH_ERROR;
}
@@ -2464,7 +2620,7 @@ int ssh_pki_signature_verify(ssh_session session,
ctx = sha256_init();
if (ctx == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Can not create SHA256CTX for input hash");
return SSH_ERROR;
}
@@ -2554,11 +2710,14 @@ ssh_string ssh_pki_do_sign(ssh_session session,
}
/* Get the session ID */
- session_id = ssh_string_new(crypto->digest_len);
+ session_id = ssh_string_new(crypto->session_id_len);
if (session_id == NULL) {
return NULL;
}
- ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ rc = ssh_string_fill(session_id, crypto->session_id, crypto->session_id_len);
+ if (rc < 0) {
+ goto end;
+ }
/* Fill the input */
sign_input = ssh_buffer_new();
@@ -2598,7 +2757,6 @@ end:
return sig_blob;
}
-#ifndef _WIN32
ssh_string ssh_pki_do_sign_agent(ssh_session session,
struct ssh_buffer_struct *buf,
const ssh_key pubkey)
@@ -2615,11 +2773,15 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session,
}
/* prepend session identifier */
- session_id = ssh_string_new(crypto->digest_len);
+ session_id = ssh_string_new(crypto->session_id_len);
if (session_id == NULL) {
return NULL;
}
- ssh_string_fill(session_id, crypto->session_id, crypto->digest_len);
+ rc = ssh_string_fill(session_id, crypto->session_id, crypto->session_id_len);
+ if (rc < 0) {
+ SSH_STRING_FREE(session_id);
+ return NULL;
+ }
sig_buf = ssh_buffer_new();
if (sig_buf == NULL) {
@@ -2648,7 +2810,6 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session,
return sig_blob;
}
-#endif /* _WIN32 */
#ifdef WITH_SERVER
ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
@@ -2656,7 +2817,7 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
const enum ssh_digest_e digest)
{
struct ssh_crypto_struct *crypto = NULL;
-
+ bool allowed;
ssh_signature sig = NULL;
ssh_string sig_blob = NULL;
@@ -2668,11 +2829,17 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
return NULL;
}
+ allowed = ssh_key_size_allowed(session, privkey);
+ if (!allowed) {
+ ssh_set_error(session, SSH_FATAL, "The hostkey size too small");
+ return NULL;
+ }
+
crypto = session->next_crypto ? session->next_crypto :
session->current_crypto;
if (crypto->secret_hash == NULL){
- ssh_set_error(session,SSH_FATAL,"Missing secret_hash");
+ ssh_set_error(session, SSH_FATAL, "Missing secret_hash");
return NULL;
}
@@ -2693,9 +2860,9 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
/* Generate the signature */
sig = pki_do_sign(privkey,
- ssh_buffer_get(sign_input),
- ssh_buffer_get_len(sign_input),
- digest);
+ ssh_buffer_get(sign_input),
+ ssh_buffer_get_len(sign_input),
+ digest);
if (sig == NULL) {
goto end;
}
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index ecde4cdd..82afdf8e 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -49,7 +49,7 @@
* code.
*
* @param[out] pkey A pointer where the allocated key can be stored. You
- * need to free the memory.
+ * need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
*
@@ -69,20 +69,20 @@ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
if (rc == SSH_ERROR){
- SSH_LOG(SSH_LOG_WARN, "Unpack error");
+ SSH_LOG(SSH_LOG_TRACE, "Unpack error");
return SSH_ERROR;
}
type = ssh_key_type_from_name(type_s);
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown key type '%s' found!", type_s);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown key type '%s' found!", type_s);
return SSH_ERROR;
}
SAFE_FREE(type_s);
rc = pki_import_privkey_buffer(type, key_blob_buffer, &key);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Failed to read key in OpenSSH format");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to read key in OpenSSH format");
goto fail;
}
@@ -133,17 +133,17 @@ static int pki_private_key_decrypt(ssh_string blob,
}
if (ciphers[i].name == NULL){
- SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported cipher %s", ciphername);
return SSH_ERROR;
}
cmp = strcmp(kdfname, "bcrypt");
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported KDF %s", kdfname);
return SSH_ERROR;
}
if (ssh_string_len(blob) % cipher.blocksize != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Encrypted string not multiple of blocksize: %zu",
ssh_string_len(blob));
return SSH_ERROR;
@@ -167,12 +167,12 @@ static int pki_private_key_decrypt(ssh_string blob,
/* We need material for key (keysize bits / 8) and IV (blocksize) */
key_material_len = cipher.keysize/8 + cipher.blocksize;
if (key_material_len > sizeof(key_material)) {
- SSH_LOG(SSH_LOG_WARN, "Key material too big");
+ SSH_LOG(SSH_LOG_TRACE, "Key material too big");
return SSH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG,
- "Decryption: %d key, %d IV, %d rounds, %zu bytes salt",
+ "Decryption: %d key, %d IV, %" PRIu32 " rounds, %zu bytes salt",
cipher.keysize/8,
cipher.blocksize,
rounds,
@@ -181,7 +181,7 @@ static int pki_private_key_decrypt(ssh_string blob,
if (passphrase == NULL) {
if (auth_fn == NULL) {
SAFE_FREE(salt);
- SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
+ SSH_LOG(SSH_LOG_TRACE, "No passphrase provided");
return SSH_ERROR;
}
rc = auth_fn("Passphrase",
@@ -251,7 +251,7 @@ ssh_pki_openssh_import(const char *text_key,
cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (no header)");
goto out;
}
ptr += strlen(OPENSSH_HEADER_BEGIN);
@@ -260,7 +260,7 @@ ssh_pki_openssh_import(const char *text_key,
}
end = strstr(ptr, OPENSSH_HEADER_END);
if (end == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (no footer)");
goto out;
}
base64 = malloc(end - ptr + 1);
@@ -277,7 +277,7 @@ ssh_pki_openssh_import(const char *text_key,
buffer = base64_to_bin(base64);
SAFE_FREE(base64);
if (buffer == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (base64 error)");
goto out;
}
rc = ssh_buffer_unpack(buffer, "PssSdSS",
@@ -290,21 +290,21 @@ ssh_pki_openssh_import(const char *text_key,
&pubkey0,
&privkeys);
if (rc == SSH_ERROR) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (unpack error)");
goto out;
}
cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
if (cmp != 0) {
- SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
+ SSH_LOG(SSH_LOG_TRACE, "Not an OpenSSH private key (bad magic)");
goto out;
}
- SSH_LOG(SSH_LOG_INFO,
- "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d",
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %" PRIu32,
ciphername,
kdfname,
nkeys);
if (nkeys != 1) {
- SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
+ SSH_LOG(SSH_LOG_TRACE, "Opening OpenSSH private key: only 1 key supported (%" PRIu32 " available)", nkeys);
goto out;
}
@@ -314,7 +314,7 @@ ssh_pki_openssh_import(const char *text_key,
if (!private) {
rc = ssh_pki_import_pubkey_blob(pubkey0, &key);
if (rc != SSH_OK) {
- SSH_LOG(SSH_LOG_WARN, "Failed to import public key blob");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to import public key blob");
}
/* in either case we clean up here */
goto out;
@@ -343,7 +343,7 @@ ssh_pki_openssh_import(const char *text_key,
rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
if (rc == SSH_ERROR || checkint1 != checkint2) {
- SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
+ SSH_LOG(SSH_LOG_TRACE, "OpenSSH private key unpack error (correct password?)");
goto out;
}
rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
@@ -358,7 +358,7 @@ ssh_pki_openssh_import(const char *text_key,
if (padding != i) {
ssh_key_free(key);
key = NULL;
- SSH_LOG(SSH_LOG_WARN, "Invalid padding");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid padding");
goto out;
}
}
@@ -395,37 +395,6 @@ ssh_key ssh_pki_openssh_pubkey_import(const char *text_key)
/** @internal
- * @brief exports a private key to a string blob.
- * @param[in] privkey private key to convert
- * @param[out] buffer buffer to write the blob in.
- * @returns SSH_OK on success
- * @warning only supports ed25519 key type at the moment.
- */
-static int pki_openssh_export_privkey_blob(const ssh_key privkey,
- ssh_buffer buffer)
-{
- int rc;
-
- if (privkey->type != SSH_KEYTYPE_ED25519) {
- SSH_LOG(SSH_LOG_WARN, "Type %s not supported", privkey->type_c);
- return SSH_ERROR;
- }
- if (privkey->ed25519_privkey == NULL ||
- privkey->ed25519_pubkey == NULL) {
- return SSH_ERROR;
- }
- rc = ssh_buffer_pack(buffer,
- "sdPdPP",
- privkey->type_c,
- (uint32_t)ED25519_KEY_LEN,
- (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey,
- (uint32_t)(2 * ED25519_KEY_LEN),
- (size_t)ED25519_KEY_LEN, privkey->ed25519_privkey,
- (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey);
- return rc;
-}
-
-/** @internal
* @brief encrypts an ed25519 private key blob
*
*/
@@ -462,29 +431,29 @@ static int pki_private_key_encrypt(ssh_buffer privkey_buffer,
}
if (ciphers[i].name == NULL){
- SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported cipher %s", ciphername);
return SSH_ERROR;
}
cmp = strcmp(kdfname, "bcrypt");
if (cmp != 0){
- SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
+ SSH_LOG(SSH_LOG_TRACE, "Unsupported KDF %s", kdfname);
return SSH_ERROR;
}
/* We need material for key (keysize bits / 8) and IV (blocksize) */
key_material_len = cipher.keysize/8 + cipher.blocksize;
if (key_material_len > sizeof(key_material)){
- SSH_LOG(SSH_LOG_WARN, "Key material too big");
+ SSH_LOG(SSH_LOG_TRACE, "Key material too big");
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_WARN, "Encryption: %d key, %d IV, %d rounds, %zu bytes salt",
+ SSH_LOG(SSH_LOG_DEBUG, "Encryption: %d key, %d IV, %" PRIu32 " rounds, %zu bytes salt",
cipher.keysize/8,
cipher.blocksize, rounds, ssh_string_len(salt));
if (passphrase == NULL){
if (auth_fn == NULL){
- SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
+ SSH_LOG(SSH_LOG_TRACE, "No passphrase provided");
return SSH_ERROR;
}
rc = auth_fn("Passphrase",
@@ -536,8 +505,8 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
ssh_auth_callback auth_fn,
void *auth_data)
{
- ssh_buffer buffer;
- ssh_string str = NULL;
+ ssh_buffer buffer = NULL;
+ ssh_string str = NULL, blob = NULL;
ssh_string pubkey_s=NULL;
ssh_buffer privkey_buffer = NULL;
uint32_t rnd;
@@ -554,17 +523,13 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
if (privkey == NULL) {
return NULL;
}
- if (privkey->type != SSH_KEYTYPE_ED25519){
- SSH_LOG(SSH_LOG_WARN, "Unsupported key type %s", privkey->type_c);
- return NULL;
- }
if (passphrase != NULL || auth_fn != NULL){
- SSH_LOG(SSH_LOG_INFO, "Enabling encryption for private key export");
+ SSH_LOG(SSH_LOG_DEBUG, "Enabling encryption for private key export");
to_encrypt = 1;
}
buffer = ssh_buffer_new();
- pubkey_s = pki_publickey_to_blob(privkey);
- if(buffer == NULL || pubkey_s == NULL){
+ rc = ssh_pki_export_pubkey_blob(privkey, &pubkey_s);
+ if (buffer == NULL || rc != SSH_OK) {
goto error;
}
@@ -578,22 +543,17 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
goto error;
}
- /* checkint1 & 2 */
- rc = ssh_buffer_pack(privkey_buffer,
- "dd",
- rnd,
- rnd);
- if (rc == SSH_ERROR){
- goto error;
- }
-
- rc = pki_openssh_export_privkey_blob(privkey, privkey_buffer);
- if (rc == SSH_ERROR){
+ rc = ssh_pki_export_privkey_blob(privkey, &blob);
+ if (rc != SSH_OK) {
goto error;
}
- /* comment */
- rc = ssh_buffer_pack(privkey_buffer, "s", "" /* comment */);
+ rc = ssh_buffer_pack(privkey_buffer,
+ "ddPs",
+ rnd, /* checkint 1 & 2 */
+ rnd,
+ ssh_string_len(blob), ssh_string_data(blob),
+ "" /* comment */);
if (rc == SSH_ERROR){
goto error;
}
@@ -630,7 +590,11 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
goto error;
}
- ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
+ rc = ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
+ if (rc != SSH_OK) {
+ SSH_BUFFER_FREE(kdf_buf);
+ goto error;
+ }
kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf));
if (kdf_options == NULL){
SSH_BUFFER_FREE(kdf_buf);
@@ -706,6 +670,8 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
}
error:
+ ssh_string_burn(blob);
+ ssh_string_free(blob);
if (privkey_buffer != NULL) {
void *bufptr = ssh_buffer_get(privkey_buffer);
explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer));
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 08409209..f4ce8bdf 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -29,14 +29,24 @@
#include "config.h"
#include "libssh/priv.h"
+#include "libcrypto-compat.h"
#include <openssl/pem.h>
-#include <openssl/dsa.h>
-#include <openssl/err.h>
-#include <openssl/engine.h>
#include <openssl/evp.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#include <openssl/dsa.h>
#include <openssl/rsa.h>
-#include "libcrypto-compat.h"
+#else
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#if defined(WITH_PKCS11_URI) && defined(WITH_PKCS11_PROVIDER)
+#include <openssl/store.h>
+#include <openssl/provider.h>
+#endif
+#endif /* OPENSSL_VERSION_NUMBER */
#ifdef HAVE_OPENSSL_EC_H
#include <openssl/ec.h>
@@ -81,12 +91,24 @@ static int pem_get_password(char *buf, int size, int rwflag, void *userdata) {
return 0;
}
+void pki_key_clean(ssh_key key)
+{
+ if (key == NULL)
+ return;
+ EVP_PKEY_free(key->key);
+ key->key = NULL;
+}
+
#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int pki_key_ecdsa_to_nid(EC_KEY *k)
{
const EC_GROUP *g = EC_KEY_get0_group(k);
int nid;
+ if (g == NULL) {
+ return -1;
+ }
nid = EC_GROUP_get_curve_name(g);
if (nid) {
return nid;
@@ -94,8 +116,30 @@ static int pki_key_ecdsa_to_nid(EC_KEY *k)
return -1;
}
+#else
+static int pki_key_ecdsa_to_nid(EVP_PKEY *k)
+{
+ char gname[25] = { 0 };
+ int rc;
+
+ rc = EVP_PKEY_get_utf8_string_param(k,
+ OSSL_PKEY_PARAM_GROUP_NAME,
+ gname,
+ 25,
+ NULL);
+ if (rc != 1) {
+ return -1;
+ }
+
+ return pki_key_ecgroup_name_to_nid(gname);
+}
+#endif /* OPENSSL_VERSION_NUMBER */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(EC_KEY *k)
+#else
+static enum ssh_keytypes_e pki_key_ecdsa_to_key_type(EVP_PKEY *k)
+#endif /* OPENSSL_VERSION_NUMBER */
{
int nid;
@@ -158,114 +202,157 @@ int pki_key_ecdsa_nid_from_name(const char *name)
return -1;
}
-static ssh_string make_ecpoint_string(const EC_GROUP *g,
- const EC_POINT *p)
+int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp)
{
- ssh_string s;
- size_t len;
-
- len = EC_POINT_point2oct(g,
- p,
- POINT_CONVERSION_UNCOMPRESSED,
- NULL,
- 0,
- NULL);
- if (len == 0) {
- return NULL;
- }
+ int rc = 0;
+ BIGNUM *bexp = NULL;
- s = ssh_string_new(len);
- if (s == NULL) {
- return NULL;
- }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_POINT *p = NULL;
+ const EC_GROUP *g = NULL;
+ EC_KEY *ecdsa = NULL;
+#else
+ const char *group_name = OSSL_EC_curve_nid2name(nid);
+ OSSL_PARAM_BLD *param_bld = NULL;
- len = EC_POINT_point2oct(g,
- p,
- POINT_CONVERSION_UNCOMPRESSED,
- ssh_string_data(s),
- ssh_string_len(s),
- NULL);
- if (len != ssh_string_len(s)) {
- SSH_STRING_FREE(s);
- return NULL;
+ if (group_name == NULL) {
+ return -1;
}
+#endif /* OPENSSL_VERSION_NUMBER */
- return s;
-}
-
-int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp)
-{
- EC_POINT *p = NULL;
- const EC_GROUP *g = NULL;
- int ok;
- BIGNUM *bexp = NULL;
+ bexp = ssh_make_string_bn(exp);
+ if (bexp == NULL) {
+ return -1;
+ }
key->ecdsa_nid = nid;
key->type_c = pki_key_ecdsa_nid_to_name(nid);
- key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (key->ecdsa == NULL) {
- return -1;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (ecdsa == NULL) {
+ rc = -1;
+ goto cleanup;
}
- g = EC_KEY_get0_group(key->ecdsa);
+ g = EC_KEY_get0_group(ecdsa);
p = EC_POINT_new(g);
if (p == NULL) {
- return -1;
+ rc = -1;
+ goto cleanup;
}
- ok = EC_POINT_oct2point(g,
+ rc = EC_POINT_oct2point(g,
p,
ssh_string_data(e),
ssh_string_len(e),
NULL);
- if (!ok) {
- EC_POINT_free(p);
- return -1;
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
}
/* EC_KEY_set_public_key duplicates p */
- ok = EC_KEY_set_public_key(key->ecdsa, p);
- EC_POINT_free(p);
- if (!ok) {
- return -1;
+ rc = EC_KEY_set_public_key(ecdsa, p);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
}
- bexp = ssh_make_string_bn(exp);
- if (bexp == NULL) {
- EC_KEY_free(key->ecdsa);
- return -1;
- }
/* EC_KEY_set_private_key duplicates exp */
- ok = EC_KEY_set_private_key(key->ecdsa, bexp);
+ rc = EC_KEY_set_private_key(ecdsa, bexp);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ /* ecdsa will be freed when the EVP_PKEY key->key is freed */
+ rc = EVP_PKEY_assign_EC_KEY(key->key, ecdsa);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+ /* ssh_key is now the owner of this memory */
+ ecdsa = NULL;
+
+ /* set rc to 0 if everything went well */
+ rc = 0;
+
+cleanup:
+ EC_KEY_free(ecdsa);
+ EC_POINT_free(p);
BN_free(bexp);
- if (!ok) {
- EC_KEY_free(key->ecdsa);
- return -1;
+ return rc;
+#else
+ param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL){
+ rc = -1;
+ goto cleanup;
}
- return 0;
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME,
+ group_name, strlen(group_name));
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY,
+ ssh_string_data(e), ssh_string_len(e));
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, bexp);
+ if (rc != 1) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = evp_build_pkey("EC", param_bld, &(key->key), EVP_PKEY_KEYPAIR);
+
+cleanup:
+ OSSL_PARAM_BLD_free(param_bld);
+ BN_free(bexp);
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
{
+ int rc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_POINT *p = NULL;
const EC_GROUP *g = NULL;
+ EC_KEY *ecdsa = NULL;
int ok;
+#else
+ const char *group_name = OSSL_EC_curve_nid2name(nid);
+ OSSL_PARAM_BLD *param_bld;
+#endif /* OPENSSL_VERSION_NUMBER */
key->ecdsa_nid = nid;
key->type_c = pki_key_ecdsa_nid_to_name(nid);
- key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (key->ecdsa == NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (ecdsa == NULL) {
return -1;
}
- g = EC_KEY_get0_group(key->ecdsa);
+ g = EC_KEY_get0_group(ecdsa);
p = EC_POINT_new(g);
if (p == NULL) {
+ EC_KEY_free(ecdsa);
return -1;
}
@@ -275,24 +362,60 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
ssh_string_len(e),
NULL);
if (!ok) {
+ EC_KEY_free(ecdsa);
EC_POINT_free(p);
return -1;
}
/* EC_KEY_set_public_key duplicates p */
- ok = EC_KEY_set_public_key(key->ecdsa, p);
+ ok = EC_KEY_set_public_key(ecdsa, p);
EC_POINT_free(p);
if (!ok) {
+ EC_KEY_free(ecdsa);
+ return -1;
+ }
+
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ EC_KEY_free(ecdsa);
+ return -1;
+ }
+
+ rc = EVP_PKEY_assign_EC_KEY(key->key, ecdsa);
+ if (rc != 1) {
+ EC_KEY_free(ecdsa);
return -1;
}
return 0;
+#else
+ param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL)
+ goto err;
+
+ rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME,
+ group_name, strlen(group_name));
+ if (rc != 1)
+ goto err;
+ rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY,
+ ssh_string_data(e), ssh_string_len(e));
+ if (rc != 1)
+ goto err;
+
+ rc = evp_build_pkey("EC", param_bld, &(key->key), EVP_PKEY_PUBLIC_KEY);
+ OSSL_PARAM_BLD_free(param_bld);
+
+ return rc;
+err:
+ OSSL_PARAM_BLD_free(param_bld);
+ return -1;
+#endif /* OPENSSL_VERSION_NUMBER */
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
ssh_key pki_key_dup(const ssh_key key, int demote)
{
- ssh_key new;
+ ssh_key new = NULL;
int rc;
new = ssh_key_new();
@@ -300,10 +423,6 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
return NULL;
}
-#ifdef WITH_PKCS11_URI
- new->key = key->key;
-#endif
-
new->type = key->type;
new->type_c = key->type_c;
if (demote) {
@@ -313,75 +432,28 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
switch (key->type) {
- case SSH_KEYTYPE_DSS: {
- const BIGNUM *p = NULL, *q = NULL, *g = NULL,
- *pub_key = NULL, *priv_key = NULL;
- BIGNUM *np, *nq, *ng, *npub_key, *npriv_key;
- new->dsa = DSA_new();
- if (new->dsa == NULL) {
- goto fail;
- }
-
- /*
- * p = public prime number
- * q = public 160-bit subprime, q | p-1
- * g = public generator of subgroup
- * pub_key = public key y = g^x
- * priv_key = private key x
- */
- DSA_get0_pqg(key->dsa, &p, &q, &g);
- np = BN_dup(p);
- nq = BN_dup(q);
- ng = BN_dup(g);
- if (np == NULL || nq == NULL || ng == NULL) {
- BN_free(np);
- BN_free(nq);
- BN_free(ng);
- goto fail;
- }
-
- /* Memory management of np, nq and ng is transferred to DSA object */
- rc = DSA_set0_pqg(new->dsa, np, nq, ng);
- if (rc == 0) {
- BN_free(np);
- BN_free(nq);
- BN_free(ng);
- goto fail;
- }
-
- DSA_get0_key(key->dsa, &pub_key, &priv_key);
- npub_key = BN_dup(pub_key);
- if (npub_key == NULL) {
- goto fail;
- }
-
- /* Memory management of npubkey is transferred to DSA object */
- rc = DSA_set0_key(new->dsa, npub_key, NULL);
- if (rc == 0) {
- goto fail;
- }
-
- if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
- npriv_key = BN_dup(priv_key);
- if (npriv_key == NULL) {
- goto fail;
- }
-
- /* Memory management of npriv_key is transferred to DSA object */
- rc = DSA_set0_key(new->dsa, NULL, npriv_key);
- if (rc == 0) {
- goto fail;
- }
- }
-
- break;
- }
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1: {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
const BIGNUM *n = NULL, *e = NULL, *d = NULL;
BIGNUM *nn, *ne, *nd;
- new->rsa = RSA_new();
- if (new->rsa == NULL) {
+ RSA *new_rsa = NULL;
+ const RSA *key_rsa = EVP_PKEY_get0_RSA(key->key);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+#ifdef WITH_PKCS11_URI
+ /* Take the PKCS#11 keys as they are */
+ if (key->flags & SSH_KEY_FLAG_PKCS11_URI && !demote) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ goto fail;
+ }
+ new->key = key->key;
+ return new;
+ }
+#endif /* WITH_PKCS11_URI */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ new_rsa = RSA_new();
+ if (new_rsa == NULL) {
goto fail;
}
@@ -395,18 +467,20 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
* dmq1 = d mod (q-1)
* iqmp = q^-1 mod p
*/
- RSA_get0_key(key->rsa, &n, &e, &d);
+ RSA_get0_key(key_rsa, &n, &e, &d);
nn = BN_dup(n);
ne = BN_dup(e);
if (nn == NULL || ne == NULL) {
+ RSA_free(new_rsa);
BN_free(nn);
BN_free(ne);
goto fail;
}
/* Memory management of nn and ne is transferred to RSA object */
- rc = RSA_set0_key(new->rsa, nn, ne, NULL);
+ rc = RSA_set0_key(new_rsa, nn, ne, NULL);
if (rc == 0) {
+ RSA_free(new_rsa);
BN_free(nn);
BN_free(ne);
goto fail;
@@ -419,43 +493,48 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
nd = BN_dup(d);
if (nd == NULL) {
+ RSA_free(new_rsa);
goto fail;
}
/* Memory management of nd is transferred to RSA object */
- rc = RSA_set0_key(new->rsa, NULL, NULL, nd);
+ rc = RSA_set0_key(new_rsa, NULL, NULL, nd);
if (rc == 0) {
+ RSA_free(new_rsa);
goto fail;
}
/* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the
* RSA operations are much faster when these values are available.
*/
- RSA_get0_factors(key->rsa, &p, &q);
+ RSA_get0_factors(key_rsa, &p, &q);
if (p != NULL && q != NULL) { /* need to set both of them */
np = BN_dup(p);
nq = BN_dup(q);
if (np == NULL || nq == NULL) {
+ RSA_free(new_rsa);
BN_free(np);
BN_free(nq);
goto fail;
}
/* Memory management of np and nq is transferred to RSA object */
- rc = RSA_set0_factors(new->rsa, np, nq);
+ rc = RSA_set0_factors(new_rsa, np, nq);
if (rc == 0) {
+ RSA_free(new_rsa);
BN_free(np);
BN_free(nq);
goto fail;
}
}
- RSA_get0_crt_params(key->rsa, &dmp1, &dmq1, &iqmp);
+ RSA_get0_crt_params(key_rsa, &dmp1, &dmq1, &iqmp);
if (dmp1 != NULL || dmq1 != NULL || iqmp != NULL) {
ndmp1 = BN_dup(dmp1);
ndmq1 = BN_dup(dmq1);
niqmp = BN_dup(iqmp);
if (ndmp1 == NULL || ndmq1 == NULL || niqmp == NULL) {
+ RSA_free(new_rsa);
BN_free(ndmp1);
BN_free(ndmq1);
BN_free(niqmp);
@@ -464,8 +543,9 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
/* Memory management of ndmp1, ndmq1 and niqmp is transferred
* to RSA object */
- rc = RSA_set0_crt_params(new->rsa, ndmp1, ndmq1, niqmp);
+ rc = RSA_set0_crt_params(new_rsa, ndmp1, ndmq1, niqmp);
if (rc == 0) {
+ RSA_free(new_rsa);
BN_free(ndmp1);
BN_free(ndmq1);
BN_free(niqmp);
@@ -474,6 +554,26 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
}
+ new->key = EVP_PKEY_new();
+ if (new->key == NULL) {
+ RSA_free(new_rsa);
+ goto fail;
+ }
+
+ rc = EVP_PKEY_assign_RSA(new->key, new_rsa);
+ if (rc != 1) {
+ EVP_PKEY_free(new->key);
+ RSA_free(new_rsa);
+ goto fail;
+ }
+
+ new_rsa = NULL;
+#else
+ rc = evp_dup_rsa_pkey(key, new, demote);
+ if (rc != SSH_OK) {
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
break;
}
case SSH_KEYTYPE_ECDSA_P256:
@@ -481,31 +581,73 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
case SSH_KEYTYPE_ECDSA_P521:
#ifdef HAVE_OPENSSL_ECC
new->ecdsa_nid = key->ecdsa_nid;
-
+#ifdef WITH_PKCS11_URI
+ /* Take the PKCS#11 keys as they are */
+ if (key->flags & SSH_KEY_FLAG_PKCS11_URI && !demote) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ goto fail;
+ }
+ new->key = key->key;
+ return new;
+ }
+#endif /* WITH_PKCS11_URI */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* privkey -> pubkey */
if (demote && ssh_key_is_private(key)) {
- const EC_POINT *p;
+ const EC_POINT *p = NULL;
+ EC_KEY *new_ecdsa = NULL, *old_ecdsa = NULL;
int ok;
- new->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (new->ecdsa == NULL) {
+ new_ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (new_ecdsa == NULL) {
goto fail;
}
- p = EC_KEY_get0_public_key(key->ecdsa);
+ old_ecdsa = EVP_PKEY_get0_EC_KEY(key->key);
+ if (old_ecdsa == NULL) {
+ EC_KEY_free(new_ecdsa);
+ goto fail;
+ }
+
+ p = EC_KEY_get0_public_key(old_ecdsa);
if (p == NULL) {
+ EC_KEY_free(new_ecdsa);
+ goto fail;
+ }
+
+ ok = EC_KEY_set_public_key(new_ecdsa, p);
+ if (ok != 1) {
+ EC_KEY_free(new_ecdsa);
goto fail;
}
- ok = EC_KEY_set_public_key(new->ecdsa, p);
- if (!ok) {
+ new->key = EVP_PKEY_new();
+ if (new->key == NULL) {
+ EC_KEY_free(new_ecdsa);
+ goto fail;
+ }
+
+ ok = EVP_PKEY_assign_EC_KEY(new->key, new_ecdsa);
+ if (ok != 1) {
+ EC_KEY_free(new_ecdsa);
goto fail;
}
} else {
- new->ecdsa = EC_KEY_dup(key->ecdsa);
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ goto fail;
+ }
+ new->key = key->key;
+ }
+#else
+ rc = evp_dup_ecdsa_pkey(key, new, demote);
+ if (rc != SSH_OK) {
+ goto fail;
}
+#endif /* OPENSSL_VERSION_NUMBER */
break;
-#endif
+#endif /* HAVE_OPENSSL_ECC */
case SSH_KEYTYPE_ED25519:
rc = pki_ed25519_key_dup(new, key);
if (rc != SSH_OK) {
@@ -525,172 +667,181 @@ fail:
}
int pki_key_generate_rsa(ssh_key key, int parameter){
- BIGNUM *e;
int rc;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ BIGNUM *e = NULL;
+ RSA *key_rsa = NULL;
+#else
+ OSSL_PARAM params[3];
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ unsigned e = 65537;
+#endif /* OPENSSL_VERSION_NUMBER */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ e = BN_new();
+ key_rsa = RSA_new();
+ if (key_rsa == NULL) {
+ return SSH_ERROR;
+ }
- e = BN_new();
- key->rsa = RSA_new();
-
- BN_set_word(e, 65537);
- rc = RSA_generate_key_ex(key->rsa, parameter, e, NULL);
+ BN_set_word(e, 65537);
+ rc = RSA_generate_key_ex(key_rsa, parameter, e, NULL);
- BN_free(e);
+ BN_free(e);
- if (rc <= 0 || key->rsa == NULL)
- return SSH_ERROR;
- return SSH_OK;
-}
+ if (rc <= 0 || key_rsa == NULL) {
+ return SSH_ERROR;
+ }
-int pki_key_generate_dss(ssh_key key, int parameter){
- int rc;
-#if OPENSSL_VERSION_NUMBER > 0x00908000L
- key->dsa = DSA_new();
- if (key->dsa == NULL) {
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ RSA_free(key_rsa);
return SSH_ERROR;
}
- rc = DSA_generate_parameters_ex(key->dsa,
- parameter,
- NULL, /* seed */
- 0, /* seed_len */
- NULL, /* counter_ret */
- NULL, /* h_ret */
- NULL); /* cb */
+
+ rc = EVP_PKEY_assign_RSA(key->key, key_rsa);
if (rc != 1) {
- DSA_free(key->dsa);
- key->dsa = NULL;
+ RSA_free(key_rsa);
+ EVP_PKEY_free(key->key);
return SSH_ERROR;
}
+
+ key_rsa = NULL;
#else
- key->dsa = DSA_generate_parameters(parameter, NULL, 0, NULL, NULL,
- NULL, NULL);
- if(key->dsa == NULL){
+ key->key = NULL;
+
+ rc = EVP_PKEY_keygen_init(pctx);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(pctx);
return SSH_ERROR;
}
-#endif
- rc = DSA_generate_key(key->dsa);
- if (rc != 1){
- DSA_free(key->dsa);
- key->dsa=NULL;
+
+ params[0] = OSSL_PARAM_construct_int("bits", &parameter);
+ params[1] = OSSL_PARAM_construct_uint("e", &e);
+ params[2] = OSSL_PARAM_construct_end();
+ rc = EVP_PKEY_CTX_set_params(pctx, params);
+ if (rc != 1) {
+ EVP_PKEY_CTX_free(pctx);
return SSH_ERROR;
}
- return SSH_OK;
+
+ rc = EVP_PKEY_generate(pctx, &(key->key));
+
+ EVP_PKEY_CTX_free(pctx);
+
+ if (rc != 1 || key->key == NULL)
+ return SSH_ERROR;
+#endif /* OPENSSL_VERSION_NUMBER */
+ return SSH_OK;
}
#ifdef HAVE_OPENSSL_ECC
-int pki_key_generate_ecdsa(ssh_key key, int parameter) {
+int pki_key_generate_ecdsa(ssh_key key, int parameter)
+{
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *ecdsa = NULL;
int ok;
-
+#else
+ const char *group_name = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
switch (parameter) {
+ case 256:
+ key->ecdsa_nid = NID_X9_62_prime256v1;
+ key->type = SSH_KEYTYPE_ECDSA_P256;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ group_name = NISTP256;
+#endif /* OPENSSL_VERSION_NUMBER */
+ break;
case 384:
key->ecdsa_nid = NID_secp384r1;
key->type = SSH_KEYTYPE_ECDSA_P384;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ group_name = NISTP384;
+#endif /* OPENSSL_VERSION_NUMBER */
break;
case 521:
key->ecdsa_nid = NID_secp521r1;
key->type = SSH_KEYTYPE_ECDSA_P521;
- break;
- case 256:
- key->ecdsa_nid = NID_X9_62_prime256v1;
- key->type = SSH_KEYTYPE_ECDSA_P256;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ group_name = NISTP521;
+#endif /* OPENSSL_VERSION_NUMBER */
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Invalid parameter %d for ECDSA key "
+ SSH_LOG(SSH_LOG_TRACE, "Invalid parameter %d for ECDSA key "
"generation", parameter);
return SSH_ERROR;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
+ if (ecdsa == NULL) {
+ return SSH_ERROR;
+ }
+ ok = EC_KEY_generate_key(ecdsa);
+ if (!ok) {
+ EC_KEY_free(ecdsa);
+ return SSH_ERROR;
+ }
+
+ EC_KEY_set_asn1_flag(ecdsa, OPENSSL_EC_NAMED_CURVE);
- key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid);
- if (key->ecdsa == NULL) {
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ EC_KEY_free(ecdsa);
return SSH_ERROR;
}
- ok = EC_KEY_generate_key(key->ecdsa);
- if (!ok) {
- EC_KEY_free(key->ecdsa);
+ ok = EVP_PKEY_assign_EC_KEY(key->key, ecdsa);
+ if (ok != 1) {
return SSH_ERROR;
}
- EC_KEY_set_asn1_flag(key->ecdsa, OPENSSL_EC_NAMED_CURVE);
+#else
+ key->key = EVP_EC_gen(group_name);
+ if (key->key == NULL) {
+ return SSH_ERROR;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
return SSH_OK;
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
+/* With OpenSSL 3.0 and higher the parameter 'what'
+ * is ignored and the comparison is done by OpenSSL
+ */
int pki_key_compare(const ssh_key k1,
const ssh_key k2,
enum ssh_keycmp_e what)
{
- switch (k1->type) {
- case SSH_KEYTYPE_DSS: {
- const BIGNUM *p1, *p2, *q1, *q2, *g1, *g2,
- *pub_key1, *pub_key2, *priv_key1, *priv_key2;
- if (DSA_size(k1->dsa) != DSA_size(k2->dsa)) {
- return 1;
- }
- DSA_get0_pqg(k1->dsa, &p1, &q1, &g1);
- DSA_get0_pqg(k2->dsa, &p2, &q2, &g2);
- if (bignum_cmp(p1, p2) != 0) {
- return 1;
- }
- if (bignum_cmp(q1, q2) != 0) {
- return 1;
- }
- if (bignum_cmp(g1, g2) != 0) {
- return 1;
- }
- DSA_get0_key(k1->dsa, &pub_key1, &priv_key1);
- DSA_get0_key(k2->dsa, &pub_key2, &priv_key2);
- if (bignum_cmp(pub_key1, pub_key2) != 0) {
- return 1;
- }
+ int rc;
- if (what == SSH_KEY_CMP_PRIVATE) {
- if (bignum_cmp(priv_key1, priv_key2) != 0) {
- return 1;
- }
- }
- break;
- }
- case SSH_KEYTYPE_RSA:
- case SSH_KEYTYPE_RSA1: {
- const BIGNUM *e1, *e2, *n1, *n2, *p1, *p2, *q1, *q2;
- if (RSA_size(k1->rsa) != RSA_size(k2->rsa)) {
- return 1;
- }
- RSA_get0_key(k1->rsa, &n1, &e1, NULL);
- RSA_get0_key(k2->rsa, &n2, &e2, NULL);
- if (bignum_cmp(e1, e2) != 0) {
- return 1;
- }
- if (bignum_cmp(n1, n2) != 0) {
- return 1;
- }
+ (void)what;
- if (what == SSH_KEY_CMP_PRIVATE) {
- RSA_get0_factors(k1->rsa, &p1, &q1);
- RSA_get0_factors(k2->rsa, &p2, &q2);
- if (bignum_cmp(p1, p2) != 0) {
- return 1;
- }
-
- if (bignum_cmp(q1, q2) != 0) {
- return 1;
- }
- }
- break;
- }
+ switch (ssh_key_type_plain(k1->type)) {
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
#ifdef HAVE_OPENSSL_ECC
{
- const EC_POINT *p1 = EC_KEY_get0_public_key(k1->ecdsa);
- const EC_POINT *p2 = EC_KEY_get0_public_key(k2->ecdsa);
- const EC_GROUP *g1 = EC_KEY_get0_group(k1->ecdsa);
- const EC_GROUP *g2 = EC_KEY_get0_group(k2->ecdsa);
+ const EC_KEY *ec1 = EVP_PKEY_get0_EC_KEY(k1->key);
+ const EC_KEY *ec2 = EVP_PKEY_get0_EC_KEY(k2->key);
+ const EC_POINT *p1 = NULL;
+ const EC_POINT *p2 = NULL;
+ const EC_GROUP *g1 = NULL;
+ const EC_GROUP *g2 = NULL;
+
+ if (ec1 == NULL || ec2 == NULL) {
+ return 1;
+ }
+
+ p1 = EC_KEY_get0_public_key(ec1);
+ p2 = EC_KEY_get0_public_key(ec2);
+ g1 = EC_KEY_get0_group(ec1);
+ g2 = EC_KEY_get0_group(ec2);
- if (p1 == NULL || p2 == NULL) {
+ if (p1 == NULL || p2 == NULL || g1 == NULL || g2 == NULL) {
return 1;
}
@@ -703,23 +854,29 @@ int pki_key_compare(const ssh_key k1,
}
if (what == SSH_KEY_CMP_PRIVATE) {
- if (bignum_cmp(EC_KEY_get0_private_key(k1->ecdsa),
- EC_KEY_get0_private_key(k2->ecdsa))) {
+ if (bignum_cmp(EC_KEY_get0_private_key(ec1),
+ EC_KEY_get0_private_key(ec2))) {
return 1;
}
}
-
break;
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
+#endif /* OPENSSL_VERSION_NUMBER */
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1:
+ rc = EVP_PKEY_eq(k1->key, k2->key);
+ if (rc != 1) {
+ return 1;
+ }
+ break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- /* ed25519 keys handled globaly */
+ /* ed25519 keys handled globally */
case SSH_KEYTYPE_UNKNOWN:
default:
return 1;
}
-
return 0;
}
@@ -740,37 +897,22 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
}
switch (key->type) {
- case SSH_KEYTYPE_DSS:
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
- goto err;
- }
-
- rc = EVP_PKEY_set1_DSA(pkey, key->dsa);
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
- goto err;
- }
-
- rc = EVP_PKEY_set1_RSA(pkey, key->rsa);
- break;
-#ifdef HAVE_ECC
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
goto err;
}
+ pkey = key->key;
+
+ /* Mark the operation as successful as for the other key types */
+ rc = 1;
- rc = EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
break;
-#endif
case SSH_KEYTYPE_ED25519:
-#ifdef HAVE_OPENSSL_ED25519
/* In OpenSSL, the input is the private key seed only, which means
* the first half of the SSH private key (the second half is the
* public key) */
@@ -787,11 +929,6 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
/* Mark the operation as successful as for the other key types */
rc = 1;
break;
-#else
- SSH_LOG(SSH_LOG_WARN, "PEM output not supported for key type ssh-ed25519");
- goto err;
-#endif
- case SSH_KEYTYPE_DSS_CERT01:
case SSH_KEYTYPE_RSA_CERT01:
case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384_CERT01:
@@ -799,11 +936,11 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d", key->type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d", key->type);
goto err;
}
if (rc != 1) {
- SSH_LOG(SSH_LOG_WARN, "Failed to initialize EVP_PKEY structure");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to initialize EVP_PKEY structure");
goto err;
}
@@ -830,6 +967,9 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
pkey = NULL;
if (rc != 1) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Failed to write private key: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
goto err;
}
@@ -840,7 +980,12 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
goto err;
}
- ssh_string_fill(blob, buf->data, buf->length);
+ rc = ssh_string_fill(blob, buf->data, buf->length);
+ if (rc < 0) {
+ ssh_string_free(blob);
+ goto err;
+ }
+
BIO_free(mem);
return blob;
@@ -857,20 +1002,13 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
void *auth_data)
{
BIO *mem = NULL;
- DSA *dsa = NULL;
- RSA *rsa = NULL;
-#ifdef HAVE_OPENSSL_ED25519
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ EC_KEY *ecdsa = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
uint8_t *ed25519 = NULL;
-#else
- ed25519_privkey *ed25519 = NULL;
-#endif
+ uint8_t *ed25519_pubkey = NULL;
ssh_key key = NULL;
enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN;
-#ifdef HAVE_OPENSSL_ECC
- EC_KEY *ecdsa = NULL;
-#else
- void *ecdsa = NULL;
-#endif
EVP_PKEY *pkey = NULL;
mem = BIO_new_mem_buf((void*)b64_key, -1);
@@ -891,53 +1029,41 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
BIO_free(mem);
if (pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
+ SSH_LOG(SSH_LOG_TRACE,
+ "Error parsing private key: %s",
ERR_error_string(ERR_get_error(), NULL));
return NULL;
}
switch (EVP_PKEY_base_id(pkey)) {
- case EVP_PKEY_DSA:
- dsa = EVP_PKEY_get1_DSA(pkey);
- if (dsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
- ERR_error_string(ERR_get_error(),NULL));
- goto fail;
- }
- type = SSH_KEYTYPE_DSS;
- break;
case EVP_PKEY_RSA:
- rsa = EVP_PKEY_get1_RSA(pkey);
- if (rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
- ERR_error_string(ERR_get_error(),NULL));
- goto fail;
- }
type = SSH_KEYTYPE_RSA;
break;
case EVP_PKEY_EC:
#ifdef HAVE_OPENSSL_ECC
- ecdsa = EVP_PKEY_get1_EC_KEY(pkey);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EVP_PKEY_get0_EC_KEY(pkey);
if (ecdsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing private key: %s",
+ SSH_LOG(SSH_LOG_TRACE,
+ "Error parsing private key: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
+#endif /* OPENSSL_VERSION_NUMBER */
/* pki_privatekey_type_from_string always returns P256 for ECDSA
* keys, so we need to figure out the correct type here */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
type = pki_key_ecdsa_to_key_type(ecdsa);
+#else
+ type = pki_key_ecdsa_to_key_type(pkey);
+#endif /* OPENSSL_VERSION_NUMBER */
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid private key.");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid private key.");
goto fail;
}
break;
-#endif
-#ifdef HAVE_OPENSSL_ED25519
+#endif /* HAVE_OPENSSL_ECC */
case EVP_PKEY_ED25519:
{
size_t key_len;
@@ -958,7 +1084,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
ed25519 = malloc(key_len);
if (ed25519 == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Out of memory");
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory");
goto fail;
}
@@ -970,17 +1096,32 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
+
+ /* length matches the private key length */
+ ed25519_pubkey = malloc(ED25519_KEY_LEN);
+ if (ed25519_pubkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "Out of memory");
+ goto fail;
+ }
+
+ evp_rc = EVP_PKEY_get_raw_public_key(pkey, (uint8_t *)ed25519_pubkey,
+ &key_len);
+ if (evp_rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to get ed25519 raw public key: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
type = SSH_KEYTYPE_ED25519;
+
}
break;
-#endif
default:
- EVP_PKEY_free(pkey);
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d",
EVP_PKEY_base_id(pkey));
+ EVP_PKEY_free(pkey);
return NULL;
}
- EVP_PKEY_free(pkey);
key = ssh_key_new();
if (key == NULL) {
@@ -990,164 +1131,207 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
key->type = type;
key->type_c = ssh_key_type_to_char(type);
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- key->dsa = dsa;
- key->rsa = rsa;
- key->ecdsa = ecdsa;
+ key->key = pkey;
key->ed25519_privkey = ed25519;
+ key->ed25519_pubkey = ed25519_pubkey;
#ifdef HAVE_OPENSSL_ECC
if (is_ecdsa_key_type(key->type)) {
- key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(ecdsa);
+#else
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(key->key);
+#endif /* OPENSSL_VERSION_NUMBER */
}
-#endif
+#endif /* HAVE_OPENSSL_ECC */
return key;
fail:
EVP_PKEY_free(pkey);
ssh_key_free(key);
- DSA_free(dsa);
- RSA_free(rsa);
-#ifdef HAVE_OPENSSL_ECC
- EC_KEY_free(ecdsa);
-#endif
-#ifdef HAVE_OPENSSL_ED25519
SAFE_FREE(ed25519);
-#endif
+ SAFE_FREE(ed25519_pubkey);
return NULL;
}
-int pki_privkey_build_dss(ssh_key key,
+int pki_privkey_build_rsa(ssh_key key,
+ ssh_string n,
+ ssh_string e,
+ ssh_string d,
+ ssh_string iqmp,
ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey,
- ssh_string privkey)
+ ssh_string q)
{
int rc;
- BIGNUM *bp, *bq, *bg, *bpub_key, *bpriv_key;
-
- key->dsa = DSA_new();
- if (key->dsa == NULL) {
+ BIGNUM *be = NULL, *bn = NULL, *bd = NULL;
+ BIGNUM *biqmp = NULL, *bp = NULL, *bq = NULL;
+ BIGNUM *aux = NULL, *d_consttime = NULL;
+ BIGNUM *bdmq1 = NULL, *bdmp1 = NULL;
+ BN_CTX *ctx = NULL;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
return SSH_ERROR;
}
+#else
+ RSA *key_rsa = RSA_new();
+ if (key_rsa == NULL) {
+ return SSH_ERROR;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ bn = ssh_make_string_bn(n);
+ be = ssh_make_string_bn(e);
+ bd = ssh_make_string_bn(d);
+ biqmp = ssh_make_string_bn(iqmp);
bp = ssh_make_string_bn(p);
bq = ssh_make_string_bn(q);
- bg = ssh_make_string_bn(g);
- bpub_key = ssh_make_string_bn(pubkey);
- bpriv_key = ssh_make_string_bn(privkey);
- if (bp == NULL || bq == NULL ||
- bg == NULL || bpub_key == NULL) {
+ if (be == NULL || bn == NULL || bd == NULL ||
+ /*biqmp == NULL ||*/ bp == NULL || bq == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
- /* Memory management of bp, qq and bg is transferred to DSA object */
- rc = DSA_set0_pqg(key->dsa, bp, bq, bg);
- if (rc == 0) {
+ /* Calculate remaining CRT parameters for OpenSSL to be happy
+ * taken from OpenSSH */
+ if ((ctx = BN_CTX_new()) == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
-
- /* Memory management of bpub_key and bpriv_key is transferred to DSA object */
- rc = DSA_set0_key(key->dsa, bpub_key, bpriv_key);
- if (rc == 0) {
+ if ((aux = BN_new()) == NULL ||
+ (bdmq1 = BN_new()) == NULL ||
+ (bdmp1 = BN_new()) == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+ if ((d_consttime = BN_dup(bd)) == NULL) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ BN_set_flags(aux, BN_FLG_CONSTTIME);
+ BN_set_flags(d_consttime, BN_FLG_CONSTTIME);
- return SSH_OK;
-fail:
- DSA_free(key->dsa);
- return SSH_ERROR;
-}
-
-int pki_pubkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey) {
- int rc;
- BIGNUM *bp = NULL, *bq = NULL, *bg = NULL, *bpub_key = NULL;
-
- key->dsa = DSA_new();
- if (key->dsa == NULL) {
- return SSH_ERROR;
+ if ((BN_sub(aux, bq, BN_value_one()) == 0) ||
+ (BN_mod(bdmq1, d_consttime, aux, ctx) == 0) ||
+ (BN_sub(aux, bp, BN_value_one()) == 0) ||
+ (BN_mod(bdmp1, d_consttime, aux, ctx) == 0)) {
+ rc = SSH_ERROR;
+ goto fail;
}
- bp = ssh_make_string_bn(p);
- bq = ssh_make_string_bn(q);
- bg = ssh_make_string_bn(g);
- bpub_key = ssh_make_string_bn(pubkey);
- if (bp == NULL || bq == NULL ||
- bg == NULL || bpub_key == NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* Memory management of be, bn and bd is transferred to RSA object */
+ rc = RSA_set0_key(key_rsa, bn, be, bd);
+ if (rc == 0) {
goto fail;
}
- /* Memory management of bp, bq and bg is transferred to DSA object */
- rc = DSA_set0_pqg(key->dsa, bp, bq, bg);
+ /* Memory management of bp and bq is transferred to RSA object */
+ rc = RSA_set0_factors(key_rsa, bp, bq);
if (rc == 0) {
goto fail;
}
- /* Memory management of npub_key is transferred to DSA object */
- rc = DSA_set0_key(key->dsa, bpub_key, NULL);
+ /* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the RSA
+ * operations are much faster when these values are available.
+ * https://www.openssl.org/docs/man1.0.2/crypto/rsa.html
+ * And OpenSSL fails to export these keys to PEM if these are missing:
+ * https://github.com/openssl/openssl/issues/21826
+ */
+ rc = RSA_set0_crt_params(key_rsa, bdmp1, bdmq1, biqmp);
if (rc == 0) {
goto fail;
}
+ bignum_safe_free(aux);
+ bignum_safe_free(d_consttime);
+
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ goto fail;
+ }
+
+ rc = EVP_PKEY_assign_RSA(key->key, key_rsa);
+ if (rc != 1) {
+ goto fail;
+ }
return SSH_OK;
fail:
- DSA_free(key->dsa);
+ RSA_free(key_rsa);
+ EVP_PKEY_free(key->key);
return SSH_ERROR;
-}
+#else
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, bn);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, be);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, bd);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
-int pki_privkey_build_rsa(ssh_key key,
- ssh_string n,
- ssh_string e,
- ssh_string d,
- UNUSED_PARAM(ssh_string iqmp),
- ssh_string p,
- ssh_string q)
-{
- int rc;
- BIGNUM *be, *bn, *bd/*, *biqmp*/, *bp, *bq;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_FACTOR1, bp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
- key->rsa = RSA_new();
- if (key->rsa == NULL) {
- return SSH_ERROR;
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_FACTOR2, bq);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
}
- bn = ssh_make_string_bn(n);
- be = ssh_make_string_bn(e);
- bd = ssh_make_string_bn(d);
- /*biqmp = ssh_make_string_bn(iqmp);*/
- bp = ssh_make_string_bn(p);
- bq = ssh_make_string_bn(q);
- if (be == NULL || bn == NULL || bd == NULL ||
- /*biqmp == NULL ||*/ bp == NULL || bq == NULL) {
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, bdmp1);
+ if (rc != 1) {
+ rc = SSH_ERROR;
goto fail;
}
- /* Memory management of be, bn and bd is transferred to RSA object */
- rc = RSA_set0_key(key->rsa, bn, be, bd);
- if (rc == 0) {
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, bdmq1);
+ if (rc != 1) {
+ rc = SSH_ERROR;
goto fail;
}
- /* Memory management of bp and bq is transferred to RSA object */
- rc = RSA_set0_factors(key->rsa, bp, bq);
- if (rc == 0) {
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, biqmp);
+ if (rc != 1) {
+ rc = SSH_ERROR;
goto fail;
}
- /* p, q, dmp1, dmq1 and iqmp may be NULL in private keys, but the RSA
- * operations are much faster when these values are available.
- * https://www.openssl.org/docs/man1.0.2/crypto/rsa.html
- */
- /* RSA_set0_crt_params(key->rsa, biqmp, NULL, NULL);
- TODO calculate missing crt_params */
+ rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_KEYPAIR);
+ if (rc != SSH_OK) {
+ SSH_LOG(SSH_LOG_WARNING,
+ "Failed to import private key: %s\n",
+ ERR_error_string(ERR_get_error(), NULL));
+ rc = SSH_ERROR;
+ goto fail;
+ }
- return SSH_OK;
fail:
- RSA_free(key->rsa);
- return SSH_ERROR;
+ OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ bignum_safe_free(bd);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+ bignum_safe_free(biqmp);
+
+ bignum_safe_free(aux);
+ bignum_safe_free(d_consttime);
+ bignum_safe_free(bdmp1);
+ bignum_safe_free(bdmq1);
+ BN_CTX_free(ctx);
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
int pki_pubkey_build_rsa(ssh_key key,
@@ -1155,31 +1339,71 @@ int pki_pubkey_build_rsa(ssh_key key,
ssh_string n) {
int rc;
BIGNUM *be = NULL, *bn = NULL;
-
- key->rsa = RSA_new();
- if (key->rsa == NULL) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
+ if (param_bld == NULL) {
return SSH_ERROR;
}
+#else
+ RSA *key_rsa = RSA_new();
+ if (key_rsa == NULL) {
+ return SSH_ERROR;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
be = ssh_make_string_bn(e);
bn = ssh_make_string_bn(n);
if (be == NULL || bn == NULL) {
+ rc = SSH_ERROR;
goto fail;
}
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
/* Memory management of bn and be is transferred to RSA object */
- rc = RSA_set0_key(key->rsa, bn, be, NULL);
+ rc = RSA_set0_key(key_rsa, bn, be, NULL);
if (rc == 0) {
goto fail;
}
+ key->key = EVP_PKEY_new();
+ if (key->key == NULL) {
+ goto fail;
+ }
+
+ rc = EVP_PKEY_assign_RSA(key->key, key_rsa);
+ if (rc != 1) {
+ goto fail;
+ }
+
return SSH_OK;
fail:
- RSA_free(key->rsa);
+ EVP_PKEY_free(key->key);
+ RSA_free(key_rsa);
return SSH_ERROR;
+#else
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, bn);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+ rc = OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, be);
+ if (rc != 1) {
+ rc = SSH_ERROR;
+ goto fail;
+ }
+
+ rc = evp_build_pkey("RSA", param_bld, &(key->key), EVP_PKEY_PUBLIC_KEY);
+
+fail:
+ OSSL_PARAM_BLD_free(param_bld);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+
+ return rc;
+#endif /* OPENSSL_VERSION_NUMBER */
}
-ssh_string pki_publickey_to_blob(const ssh_key key)
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
{
ssh_buffer buffer;
ssh_string type_s;
@@ -1189,7 +1413,15 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
ssh_string p = NULL;
ssh_string g = NULL;
ssh_string q = NULL;
+ ssh_string d = NULL;
+ ssh_string iqmp = NULL;
int rc;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ BIGNUM *bp = NULL, *bq = NULL, *bg = NULL, *bpub_key = NULL,
+ *bn = NULL, *be = NULL,
+ *bd = NULL, *biqmp = NULL;
+ OSSL_PARAM *params = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
buffer = ssh_buffer_new();
if (buffer == NULL) {
@@ -1219,62 +1451,37 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
}
switch (key->type) {
- case SSH_KEYTYPE_DSS: {
- const BIGNUM *bp, *bq, *bg, *bpub_key;
- DSA_get0_pqg(key->dsa, &bp, &bq, &bg);
- p = ssh_make_bignum_string((BIGNUM *)bp);
- if (p == NULL) {
- goto fail;
- }
-
- q = ssh_make_bignum_string((BIGNUM *)bq);
- if (q == NULL) {
- goto fail;
- }
-
- g = ssh_make_bignum_string((BIGNUM *)bg);
- if (g == NULL) {
- goto fail;
- }
-
- DSA_get0_key(key->dsa, &bpub_key, NULL);
- n = ssh_make_bignum_string((BIGNUM *)bpub_key);
- if (n == NULL) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA1: {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const BIGNUM *be = NULL, *bn = NULL;
+ const RSA *key_rsa = EVP_PKEY_get0_RSA(key->key);
+ RSA_get0_key(key_rsa, &bn, &be, NULL);
+#else
+ const OSSL_PARAM *out_param = NULL;
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, &params);
+ if (rc != 1) {
goto fail;
}
-
- if (ssh_buffer_add_ssh_string(buffer, p) < 0) {
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param E has been found");
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, q) < 0) {
+ rc = OSSL_PARAM_get_BN(out_param, &be);
+ if (rc != 1) {
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, g) < 0) {
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param N has been found");
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
+ rc = OSSL_PARAM_get_BN(out_param, &bn);
+ if (rc != 1) {
goto fail;
}
-
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- p = NULL;
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- g = NULL;
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- q = NULL;
- ssh_string_burn(n);
- SSH_STRING_FREE(n);
- n = NULL;
-
- break;
- }
- case SSH_KEYTYPE_RSA:
- case SSH_KEYTYPE_RSA1: {
- const BIGNUM *be, *bn;
- RSA_get0_key(key->rsa, &bn, &be, NULL);
+#endif /* OPENSSL_VERSION_NUMBER */
e = ssh_make_bignum_string((BIGNUM *)be);
if (e == NULL) {
goto fail;
@@ -1285,31 +1492,165 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, e) < 0) {
- goto fail;
- }
- if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
- goto fail;
- }
+ if (type == SSH_KEY_PUBLIC) {
+ /* The N and E parts are swapped in the public key export ! */
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ } else if (type == SSH_KEY_PRIVATE) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const BIGNUM *bd, *biqmp, *bp, *bq;
+ RSA_get0_key(key_rsa, NULL, NULL, &bd);
+ RSA_get0_factors(key_rsa, &bp, &bq);
+ RSA_get0_crt_params(key_rsa, NULL, NULL, &biqmp);
+#else
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, &params);
+ if (rc != 1) {
+ goto fail;
+ }
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param D has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &bd);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR1);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param P has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &bp);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_FACTOR2);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param Q has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &bq);
+ if (rc != 1) {
+ goto fail;
+ }
+
+ out_param = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_COEFFICIENT1);
+ if (out_param == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA: No param IQMP has been found");
+ goto fail;
+ }
+ rc = OSSL_PARAM_get_BN(out_param, &biqmp);
+ if (rc != 1) {
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ d = ssh_make_bignum_string((BIGNUM *)bd);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ iqmp = ssh_make_bignum_string((BIGNUM *)biqmp);
+ if (iqmp == NULL) {
+ goto fail;
+ }
+
+ p = ssh_make_bignum_string((BIGNUM *)bp);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ q = ssh_make_bignum_string((BIGNUM *)bq);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, iqmp);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, p);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, q);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ ssh_string_burn(iqmp);
+ SSH_STRING_FREE(iqmp);
+ iqmp = NULL;
+ ssh_string_burn(p);
+ SSH_STRING_FREE(p);
+ p = NULL;
+ ssh_string_burn(q);
+ SSH_STRING_FREE(q);
+ q = NULL;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(bd);
+ bignum_safe_free(biqmp);
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+#endif /* OPENSSL_VERSION_NUMBER */
+ }
ssh_string_burn(e);
SSH_STRING_FREE(e);
e = NULL;
ssh_string_burn(n);
SSH_STRING_FREE(n);
n = NULL;
-
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
break;
}
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- rc = pki_ed25519_public_key_to_blob(buffer, key);
- if (rc == SSH_ERROR){
- goto fail;
- }
- if (key->type == SSH_KEYTYPE_SK_ED25519 &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PUBLIC) {
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR){
+ goto fail;
+ }
+ /* public key can contain certificate sk information */
+ if (key->type == SSH_KEYTYPE_SK_ED25519) {
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+ } else {
+ rc = pki_ed25519_private_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR){
+ goto fail;
+ }
}
break;
case SSH_KEYTYPE_ECDSA_P256:
@@ -1317,50 +1658,150 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
case SSH_KEYTYPE_ECDSA_P521:
case SSH_KEYTYPE_SK_ECDSA:
#ifdef HAVE_OPENSSL_ECC
- type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid));
- if (type_s == NULL) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
- }
+ {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EC_GROUP *group = NULL;
+ EC_POINT *point = NULL;
+ const void *pubkey;
+ size_t pubkey_len;
+ OSSL_PARAM *locate_param = NULL;
+#else
+ const EC_GROUP *group = NULL;
+ const EC_POINT *point = NULL;
+ const BIGNUM *exp = NULL;
+ EC_KEY *ec = NULL;
+#endif /* OPENSSL_VERSION_NUMBER */
+
+ type_s = ssh_string_from_char(pki_key_ecdsa_nid_to_char(key->ecdsa_nid));
+ if (type_s == NULL) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
- rc = ssh_buffer_add_ssh_string(buffer, type_s);
- SSH_STRING_FREE(type_s);
- if (rc < 0) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
- }
+ rc = ssh_buffer_add_ssh_string(buffer, type_s);
+ SSH_STRING_FREE(type_s);
+ if (rc < 0) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ec = EVP_PKEY_get0_EC_KEY(key->key);
+ if (ec == NULL) {
+ goto fail;
+ }
#ifdef WITH_PKCS11_URI
- if (ssh_key_is_private(key) && !EC_KEY_get0_public_key(key->ecdsa)) {
- SSH_LOG(SSH_LOG_INFO, "It is mandatory to have separate public"
- " ECDSA key objects in the PKCS #11 device. Unlike RSA,"
- " ECDSA public keys cannot be derived from their private keys.");
- goto fail;
- }
-#endif
- e = make_ecpoint_string(EC_KEY_get0_group(key->ecdsa),
- EC_KEY_get0_public_key(key->ecdsa));
- if (e == NULL) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
- }
+ if (ssh_key_is_private(key) && !EC_KEY_get0_public_key(ec)) {
+ SSH_LOG(SSH_LOG_TRACE, "It is mandatory to have separate"
+ " public ECDSA key objects in the PKCS #11 device."
+ " Unlike RSA, ECDSA public keys cannot be derived"
+ " from their private keys.");
+ goto fail;
+ }
+#endif /* WITH_PKCS11_URI */
+ group = EC_KEY_get0_group(ec);
+ point = EC_KEY_get0_public_key(ec);
+ if (group == NULL || point == NULL) {
+ goto fail;
+ }
+ e = pki_key_make_ecpoint_string(group, point);
+#else
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, &params);
+ if (rc < 0) {
+ goto fail;
+ }
- rc = ssh_buffer_add_ssh_string(buffer, e);
- if (rc < 0) {
- goto fail;
- }
+ locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
+#ifdef WITH_PKCS11_URI
+ if (ssh_key_is_private(key) && !locate_param) {
+ SSH_LOG(SSH_LOG_TRACE, "It is mandatory to have separate"
+ " public ECDSA key objects in the PKCS #11 device."
+ " Unlike RSA, ECDSA public keys cannot be derived"
+ " from their private keys.");
+ goto fail;
+ }
+#endif /* WITH_PKCS11_URI */
- ssh_string_burn(e);
- SSH_STRING_FREE(e);
- e = NULL;
+ rc = OSSL_PARAM_get_octet_string_ptr(locate_param, &pubkey, &pubkey_len);
+ if (rc != 1) {
+ goto fail;
+ }
+ /* Convert the data to low-level representation */
+ group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, key->ecdsa_nid);
+ point = EC_POINT_new(group);
+ rc = EC_POINT_oct2point(group, point, pubkey, pubkey_len, NULL);
+ if (group == NULL || point == NULL || rc != 1) {
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+ goto fail;
+ }
- if (key->type == SSH_KEYTYPE_SK_ECDSA &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
- }
+ e = pki_key_make_ecpoint_string(group, point);
+ EC_GROUP_free(group);
+ EC_POINT_free(point);
+#endif /* OPENSSL_VERSION_NUMBER */
+ if (e == NULL) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
- break;
-#endif
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(e);
+ SSH_STRING_FREE(e);
+ e = NULL;
+ if (type == SSH_KEY_PRIVATE) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ rc = EVP_PKEY_todata(key->key, EVP_PKEY_KEYPAIR, &params);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ rc = OSSL_PARAM_get_BN(locate_param, &bd);
+ if (rc != 1) {
+ goto fail;
+ }
+ d = ssh_make_bignum_string((BIGNUM *)bd);
+ if (d == NULL) {
+ goto fail;
+ }
+ if (ssh_buffer_add_ssh_string(buffer, d) < 0) {
+ goto fail;
+ }
+#else
+ exp = EC_KEY_get0_private_key(ec);
+ if (exp == NULL) {
+ goto fail;
+ }
+ d = ssh_make_bignum_string((BIGNUM *)exp);
+ if (d == NULL) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ } else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
+ /* public key can contain certificate sk information */
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
+ break;
+ }
+#endif /* HAVE_OPENSSL_ECC */
case SSH_KEYTYPE_UNKNOWN:
default:
goto fail;
@@ -1393,91 +1834,25 @@ fail:
SSH_STRING_FREE(q);
ssh_string_burn(n);
SSH_STRING_FREE(n);
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ ssh_string_burn(iqmp);
+ SSH_STRING_FREE(iqmp);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ bignum_safe_free(bp);
+ bignum_safe_free(bq);
+ bignum_safe_free(bg);
+ bignum_safe_free(bpub_key);
+ bignum_safe_free(bn);
+ bignum_safe_free(be);
+ bignum_safe_free(bd);
+ bignum_safe_free(biqmp);
+ OSSL_PARAM_free(params);
+#endif /* OPENSSL_VERSION_NUMBER */
return NULL;
}
-static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
-{
- char buffer[40] = { 0 };
- ssh_string sig_blob = NULL;
- const BIGNUM *pr = NULL, *ps = NULL;
-
- ssh_string r = NULL;
- int r_len, r_offset_in, r_offset_out;
-
- ssh_string s = NULL;
- int s_len, s_offset_in, s_offset_out;
-
- const unsigned char *raw_sig_data = NULL;
- size_t raw_sig_len;
-
- DSA_SIG *dsa_sig;
-
- if (sig == NULL || sig->raw_sig == NULL) {
- return NULL;
- }
- raw_sig_data = ssh_string_data(sig->raw_sig);
- if (raw_sig_data == NULL) {
- return NULL;
- }
- raw_sig_len = ssh_string_len(sig->raw_sig);
-
- dsa_sig = d2i_DSA_SIG(NULL, &raw_sig_data, raw_sig_len);
- if (dsa_sig == NULL) {
- return NULL;
- }
-
- DSA_SIG_get0(dsa_sig, &pr, &ps);
- if (pr == NULL || ps == NULL) {
- goto error;
- }
-
- r = ssh_make_bignum_string((BIGNUM *)pr);
- if (r == NULL) {
- goto error;
- }
-
- s = ssh_make_bignum_string((BIGNUM *)ps);
- if (s == NULL) {
- goto error;
- }
-
- r_len = ssh_string_len(r);
- r_offset_in = (r_len > 20) ? (r_len - 20) : 0;
- r_offset_out = (r_len < 20) ? (20 - r_len) : 0;
-
- s_len = ssh_string_len(s);
- s_offset_in = (s_len > 20) ? (s_len - 20) : 0;
- s_offset_out = (s_len < 20) ? (20 - s_len) : 0;
-
- memcpy(buffer + r_offset_out,
- ((char *)ssh_string_data(r)) + r_offset_in,
- r_len - r_offset_in);
- memcpy(buffer + 20 + s_offset_out,
- ((char *)ssh_string_data(s)) + s_offset_in,
- s_len - s_offset_in);
-
- DSA_SIG_free(dsa_sig);
- SSH_STRING_FREE(r);
- SSH_STRING_FREE(s);
-
- sig_blob = ssh_string_new(40);
- if (sig_blob == NULL) {
- return NULL;
- }
-
- ssh_string_fill(sig_blob, buffer, 40);
-
- return sig_blob;
-
-error:
- DSA_SIG_free(dsa_sig);
- SSH_STRING_FREE(r);
- SSH_STRING_FREE(s);
- return NULL;
-}
-
static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig)
{
ssh_string r = NULL;
@@ -1544,7 +1919,10 @@ static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig)
goto error;
}
- ssh_string_fill(sig_blob, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
+ rc = ssh_string_fill(sig_blob, ssh_buffer_get(buf), ssh_buffer_get_len(buf));
+ if (rc < 0) {
+ goto error;
+ }
SSH_STRING_FREE(r);
SSH_STRING_FREE(s);
@@ -1554,6 +1932,7 @@ static ssh_string pki_ecdsa_signature_to_blob(const ssh_signature sig)
return sig_blob;
error:
+ SSH_STRING_FREE(sig_blob);
SSH_STRING_FREE(r);
SSH_STRING_FREE(s);
ECDSA_SIG_free(ecdsa_sig);
@@ -1566,9 +1945,6 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
ssh_string sig_blob = NULL;
switch(sig->type) {
- case SSH_KEYTYPE_DSS:
- sig_blob = pki_dsa_signature_to_blob(sig);
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
sig_blob = ssh_string_copy(sig->raw_sig);
@@ -1582,10 +1958,10 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
#ifdef HAVE_OPENSSL_ECC
sig_blob = pki_ecdsa_signature_to_blob(sig);
break;
-#endif
+#endif /* HAVE_OPENSSL_ECC */
default:
case SSH_KEYTYPE_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %s", sig->type_c);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %s", sig->type_c);
return NULL;
}
@@ -1604,14 +1980,25 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey,
size_t rsalen = 0;
size_t len = ssh_string_len(sig_blob);
- if (pubkey->rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Pubkey RSA field NULL");
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const RSA *rsa = EVP_PKEY_get0_RSA(pubkey->key);
+
+ if (rsa == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "RSA field NULL");
+ goto errout;
+ }
+
+ rsalen = RSA_size(rsa);
+#else
+ if (EVP_PKEY_get_base_id(pubkey->key) != EVP_PKEY_RSA) {
+ SSH_LOG(SSH_LOG_TRACE, "Key has no RSA pubkey");
goto errout;
}
- rsalen = RSA_size(pubkey->rsa);
+ rsalen = EVP_PKEY_size(pubkey->key);
+#endif /* OPENSSL_VERSION_NUMBER */
if (len > rsalen) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature is too big: %lu > %lu",
(unsigned long)len,
(unsigned long)rsalen);
@@ -1619,9 +2006,9 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey,
}
#ifdef DEBUG_CRYPTO
- SSH_LOG(SSH_LOG_WARN, "RSA signature len: %lu", (unsigned long)len);
+ SSH_LOG(SSH_LOG_DEBUG, "RSA signature len: %lu", (unsigned long)len);
ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len);
-#endif
+#endif /* DEBUG_CRYPTO */
if (len == rsalen) {
sig->raw_sig = ssh_string_copy(sig_blob);
@@ -1661,125 +2048,6 @@ errout:
return SSH_ERROR;
}
-static int pki_signature_from_dsa_blob(UNUSED_PARAM(const ssh_key pubkey),
- const ssh_string sig_blob,
- ssh_signature sig)
-{
- DSA_SIG *dsa_sig = NULL;
- BIGNUM *pr = NULL, *ps = NULL;
-
- ssh_string r;
- ssh_string s;
-
- size_t len;
-
- int raw_sig_len = 0;
- unsigned char *raw_sig_data = NULL;
- unsigned char *temp_raw_sig = NULL;
-
- int rc;
-
- len = ssh_string_len(sig_blob);
-
- /* 40 is the dual signature blob len. */
- if (len != 40) {
- SSH_LOG(SSH_LOG_WARN,
- "Signature has wrong size: %lu",
- (unsigned long)len);
- goto error;
- }
-
-#ifdef DEBUG_CRYPTO
- ssh_log_hexdump("r", ssh_string_data(sig_blob), 20);
- ssh_log_hexdump("s", (unsigned char *)ssh_string_data(sig_blob) + 20, 20);
-#endif
-
- r = ssh_string_new(20);
- if (r == NULL) {
- goto error;
- }
- ssh_string_fill(r, ssh_string_data(sig_blob), 20);
-
- pr = ssh_make_string_bn(r);
- ssh_string_burn(r);
- SSH_STRING_FREE(r);
- if (pr == NULL) {
- goto error;
- }
-
- s = ssh_string_new(20);
- if (s == NULL) {
- goto error;
- }
- ssh_string_fill(s, (char *)ssh_string_data(sig_blob) + 20, 20);
-
- ps = ssh_make_string_bn(s);
- ssh_string_burn(s);
- SSH_STRING_FREE(s);
- if (ps == NULL) {
- goto error;
- }
-
- dsa_sig = DSA_SIG_new();
- if (dsa_sig == NULL) {
- goto error;
- }
-
- /* Memory management of pr and ps is transferred to DSA signature
- * object */
- rc = DSA_SIG_set0(dsa_sig, pr, ps);
- if (rc == 0) {
- goto error;
- }
- ps = NULL;
- pr = NULL;
-
- /* Get the expected size of the buffer */
- rc = i2d_DSA_SIG(dsa_sig, NULL);
- if (rc <= 0) {
- goto error;
- }
- raw_sig_len = rc;
-
- raw_sig_data = (unsigned char *)calloc(1, raw_sig_len);
- if (raw_sig_data == NULL) {
- goto error;
- }
- temp_raw_sig = raw_sig_data;
-
- /* It is necessary to use a temporary pointer as i2d_* "advances" the
- * pointer */
- raw_sig_len = i2d_DSA_SIG(dsa_sig, &temp_raw_sig);
- if (raw_sig_len <= 0) {
- goto error;
- }
-
- sig->raw_sig = ssh_string_new(raw_sig_len);
- if (sig->raw_sig == NULL) {
- explicit_bzero(raw_sig_data, raw_sig_len);
- goto error;
- }
-
- rc = ssh_string_fill(sig->raw_sig, raw_sig_data, raw_sig_len);
- if (rc < 0) {
- explicit_bzero(raw_sig_data, raw_sig_len);
- goto error;
- }
-
- explicit_bzero(raw_sig_data, raw_sig_len);
- SAFE_FREE(raw_sig_data);
- DSA_SIG_free(dsa_sig);
-
- return SSH_OK;
-
-error:
- bignum_safe_free(ps);
- bignum_safe_free(pr);
- SAFE_FREE(raw_sig_data);
- DSA_SIG_free(dsa_sig);
- return SSH_ERROR;
-}
-
static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey),
const ssh_string sig_blob,
ssh_signature sig)
@@ -1838,7 +2106,7 @@ static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey),
if (rlen != 0) {
ssh_string_burn(s);
SSH_STRING_FREE(s);
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature has remaining bytes in inner "
"sigblob: %lu",
(unsigned long)rlen);
@@ -1927,7 +2195,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
int rc;
if (ssh_key_type_plain(pubkey->type) != type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Incompatible public key provided (%d) expecting (%d)",
type,
pubkey->type);
@@ -1944,12 +2212,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
sig->hash_type = hash_type;
switch(type) {
- case SSH_KEYTYPE_DSS:
- rc = pki_signature_from_dsa_blob(pubkey, sig_blob, sig);
- if (rc != SSH_OK) {
- goto error;
- }
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
rc = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
@@ -1981,7 +2243,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
#endif
default:
case SSH_KEYTYPE_UNKNOWN:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature type");
goto error;
}
@@ -2024,44 +2286,12 @@ static const EVP_MD *pki_digest_to_md(enum ssh_digest_e hash_type)
static EVP_PKEY *pki_key_to_pkey(ssh_key key)
{
EVP_PKEY *pkey = NULL;
+ int rc = 0;
-#ifdef WITH_PKCS11_URI
- if (key->flags & SSH_KEY_FLAG_PKCS11_URI) {
- pkey = key->key;
- return pkey;
- }
-#endif
-
- switch(key->type) {
- case SSH_KEYTYPE_DSS:
- case SSH_KEYTYPE_DSS_CERT01:
- if (key->dsa == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "NULL key->dsa");
- goto error;
- }
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "Out of memory");
- return NULL;
- }
-
- EVP_PKEY_set1_DSA(pkey, key->dsa);
- break;
+ switch (key->type) {
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_RSA_CERT01:
- if (key->rsa == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "NULL key->rsa");
- goto error;
- }
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "Out of memory");
- return NULL;
- }
-
- EVP_PKEY_set1_RSA(pkey, key->rsa);
- break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
@@ -2070,25 +2300,21 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key)
case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
-# if defined(HAVE_OPENSSL_ECC)
- if (key->ecdsa == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "NULL key->ecdsa");
+ if (key->key == NULL) {
+ SSH_LOG(SSH_LOG_TRACE, "NULL key->key");
goto error;
}
- pkey = EVP_PKEY_new();
- if (pkey == NULL) {
- SSH_LOG(SSH_LOG_TRACE, "Out of memory");
+ rc = EVP_PKEY_up_ref(key->key);
+ if (rc != 1) {
+ SSH_LOG(SSH_LOG_TRACE, "Failed to reference EVP_PKEY");
return NULL;
}
-
- EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa);
+ pkey = key->key;
break;
-# endif
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01:
-# if defined(HAVE_OPENSSL_ED25519)
if (ssh_key_is_private(key)) {
if (key->ed25519_privkey == NULL) {
SSH_LOG(SSH_LOG_TRACE, "NULL key->ed25519_privkey");
@@ -2116,7 +2342,6 @@ static EVP_PKEY *pki_key_to_pkey(ssh_key key)
return NULL;
}
break;
-#endif
case SSH_KEYTYPE_UNKNOWN:
default:
SSH_LOG(SSH_LOG_TRACE, "Unknown private key algorithm for type: %d",
@@ -2134,7 +2359,7 @@ error:
/**
* @internal
*
- * @brief Sign the given input data. The digest of to be signed is calculated
+ * @brief Sign the given input data. The digest to be signed is calculated
* internally as necessary.
*
* @param[in] privkey The private key to be used for signing.
@@ -2172,14 +2397,6 @@ ssh_signature pki_sign_data(const ssh_key privkey,
return NULL;
}
-#ifndef HAVE_OPENSSL_ED25519
- if (privkey->type == SSH_KEYTYPE_ED25519 ||
- privkey->type == SSH_KEYTYPE_ED25519_CERT01)
- {
- return pki_do_sign_hash(privkey, input, input_len, hash_type);
- }
-#endif
-
/* Set hash algorithm to be used */
md = pki_digest_to_md(hash_type);
if (md == NULL) {
@@ -2203,7 +2420,7 @@ ssh_signature pki_sign_data(const ssh_key privkey,
}
/* Create the context */
- ctx = EVP_MD_CTX_create();
+ ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
SSH_LOG(SSH_LOG_TRACE, "Out of memory");
goto out;
@@ -2218,7 +2435,6 @@ ssh_signature pki_sign_data(const ssh_key privkey,
goto out;
}
-#ifdef HAVE_OPENSSL_EVP_DIGESTSIGN
rc = EVP_DigestSign(ctx, raw_sig_data, &raw_sig_len, input, input_len);
if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE,
@@ -2226,23 +2442,6 @@ ssh_signature pki_sign_data(const ssh_key privkey,
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
-#else
- rc = EVP_DigestSignUpdate(ctx, input, input_len);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "EVP_DigestSignUpdate() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
-
- rc = EVP_DigestSignFinal(ctx, raw_sig_data, &raw_sig_len);
- if (rc != 1) {
- SSH_LOG(SSH_LOG_TRACE,
- "EVP_DigestSignFinal() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto out;
- }
-#endif
#ifdef DEBUG_CRYPTO
ssh_log_hexdump("Generated signature", raw_sig_data, raw_sig_len);
@@ -2280,9 +2479,7 @@ out:
explicit_bzero(raw_sig_data, raw_sig_len);
}
SAFE_FREE(raw_sig_data);
- if (pkey != NULL) {
- EVP_PKEY_free(pkey);
- }
+ EVP_PKEY_free(pkey);
return sig;
}
@@ -2311,15 +2508,15 @@ int pki_verify_data_signature(ssh_signature signature,
unsigned char *raw_sig_data = NULL;
unsigned int raw_sig_len;
+ /* Function return code
+ * Do not change this variable throughout the function until the signature
+ * is successfully verified!
+ */
int rc = SSH_ERROR;
- int evp_rc;
+ int ok;
if (pubkey == NULL || ssh_key_is_private(pubkey) || input == NULL ||
- signature == NULL || (signature->raw_sig == NULL
-#ifndef HAVE_OPENSSL_ED25519
- && signature->ed25519_sig == NULL
-#endif
- ))
+ signature == NULL || signature->raw_sig == NULL)
{
SSH_LOG(SSH_LOG_TRACE, "Bad parameter provided to "
"pki_verify_data_signature()");
@@ -2327,21 +2524,11 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Check if public key and hash type are compatible */
- rc = pki_key_check_hash_compatible(pubkey, signature->hash_type);
- if (rc != SSH_OK) {
+ ok = pki_key_check_hash_compatible(pubkey, signature->hash_type);
+ if (ok != SSH_OK) {
return SSH_ERROR;
}
-#ifndef HAVE_OPENSSL_ED25519
- if (pubkey->type == SSH_KEYTYPE_ED25519 ||
- pubkey->type == SSH_KEYTYPE_ED25519_CERT01 ||
- pubkey->type == SSH_KEYTYPE_SK_ED25519 ||
- pubkey->type == SSH_KEYTYPE_SK_ED25519_CERT01)
- {
- return pki_ed25519_verify(pubkey, signature, input, input_len);
- }
-#endif
-
/* Get the signature to be verified */
raw_sig_data = ssh_string_data(signature->raw_sig);
raw_sig_len = ssh_string_len(signature->raw_sig);
@@ -2364,7 +2551,7 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Create the context */
- ctx = EVP_MD_CTX_create();
+ ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to create EVP_MD_CTX: %s",
@@ -2373,48 +2560,69 @@ int pki_verify_data_signature(ssh_signature signature,
}
/* Verify the signature */
- evp_rc = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey);
- if (evp_rc != 1){
+ ok = EVP_DigestVerifyInit(ctx, NULL, md, NULL, pkey);
+ if (ok != 1){
SSH_LOG(SSH_LOG_TRACE,
"EVP_DigestVerifyInit() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
-#ifdef HAVE_OPENSSL_EVP_DIGESTVERIFY
- evp_rc = EVP_DigestVerify(ctx, raw_sig_data, raw_sig_len, input, input_len);
-#else
- evp_rc = EVP_DigestVerifyUpdate(ctx, input, input_len);
- if (evp_rc != 1) {
+ ok = EVP_DigestVerify(ctx, raw_sig_data, raw_sig_len, input, input_len);
+ if (ok != 1) {
SSH_LOG(SSH_LOG_TRACE,
- "EVP_DigestVerifyUpdate() failed: %s",
+ "Signature invalid: %s",
ERR_error_string(ERR_get_error(), NULL));
goto out;
}
- evp_rc = EVP_DigestVerifyFinal(ctx, raw_sig_data, raw_sig_len);
-#endif
- if (evp_rc == 1) {
- SSH_LOG(SSH_LOG_TRACE, "Signature valid");
- rc = SSH_OK;
- } else {
- SSH_LOG(SSH_LOG_TRACE,
- "Signature invalid: %s",
- ERR_error_string(ERR_get_error(), NULL));
- rc = SSH_ERROR;
- }
+ SSH_LOG(SSH_LOG_TRACE, "Signature valid");
+ rc = SSH_OK;
out:
- if (ctx != NULL) {
- EVP_MD_CTX_free(ctx);
- }
- if (pkey != NULL) {
+ EVP_MD_CTX_free(ctx);
+ EVP_PKEY_free(pkey);
+ return rc;
+}
+
+int ssh_key_size(ssh_key key)
+{
+ int bits = 0;
+ EVP_PKEY *pkey = NULL;
+
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ pkey = pki_key_to_pkey(key);
+ if (pkey == NULL) {
+ return SSH_ERROR;
+ }
+ bits = EVP_PKEY_bits(pkey);
EVP_PKEY_free(pkey);
+ return bits;
+ case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys have fixed size */
+ return 255;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_UNKNOWN:
+ default:
+ return SSH_ERROR;
}
- return rc;
}
-#ifdef HAVE_OPENSSL_ED25519
int pki_key_generate_ed25519(ssh_key key)
{
int evp_rc;
@@ -2499,50 +2707,21 @@ error:
return SSH_ERROR;
}
-#else
-ssh_signature pki_do_sign_hash(const ssh_key privkey,
- const unsigned char *hash,
- size_t hlen,
- enum ssh_digest_e hash_type)
-{
- ssh_signature sig = NULL;
- int rc;
-
- sig = ssh_signature_new();
- if (sig == NULL) {
- return NULL;
- }
-
- sig->type = privkey->type;
- sig->type_c = ssh_key_signature_to_char(privkey->type, hash_type);
- sig->hash_type = hash_type;
- switch(privkey->type) {
- case SSH_KEYTYPE_ED25519:
- rc = pki_ed25519_sign(privkey, sig, hash, hlen);
- if (rc != SSH_OK) {
- ssh_signature_free(sig);
- return NULL;
- }
- break;
- default:
- ssh_signature_free(sig);
- return NULL;
- }
-
- return sig;
-}
-#endif /* HAVE_OPENSSL_ED25519 */
+#ifdef WITH_PKCS11_URI
+#ifdef WITH_PKCS11_PROVIDER
+static bool pkcs11_provider_failed = false;
+#endif
/**
* @internal
*
- * @brief Populate the public/private ssh_key from the engine with
+ * @brief Populate the public/private ssh_key from the engine/provider with
* PKCS#11 URIs as the look up.
*
* @param[in] uri_name The PKCS#11 URI
* @param[in] nkey The ssh-key context for
- * the key loaded from the engine.
+ * the key loaded from the engine/provider.
* @param[in] key_type The type of the key used. Public/Private.
*
* @return SSH_OK if ssh-key is valid; SSH_ERROR otherwise.
@@ -2551,64 +2730,112 @@ int pki_uri_import(const char *uri_name,
ssh_key *nkey,
enum ssh_key_e key_type)
{
- ENGINE *engine = NULL;
EVP_PKEY *pkey = NULL;
- RSA *rsa = NULL;
ssh_key key = NULL;
enum ssh_keytypes_e type = SSH_KEYTYPE_UNKNOWN;
-#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L && HAVE_OPENSSL_ECC
EC_KEY *ecdsa = NULL;
-#else
- void *ecdsa = NULL;
#endif
- int ok;
-
- ENGINE_load_builtin_engines();
+#ifndef WITH_PKCS11_PROVIDER
+ ENGINE *engine = NULL;
- engine = ENGINE_by_id("pkcs11");
+ /* Do the init only once */
+ engine = pki_get_engine();
if (engine == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Could not load the engine: %s",
- ERR_error_string(ERR_get_error(),NULL));
- return SSH_ERROR;
- }
- SSH_LOG(SSH_LOG_INFO, "Engine loaded successfully");
-
- ok = ENGINE_init(engine);
- if (!ok) {
- SSH_LOG(SSH_LOG_WARN,
- "Could not initialize the engine: %s",
- ERR_error_string(ERR_get_error(),NULL));
- ENGINE_free(engine);
- return SSH_ERROR;
+ SSH_LOG(SSH_LOG_TRACE, "Failed to initialize engine");
+ goto fail;
}
- SSH_LOG(SSH_LOG_INFO, "Engine init success");
-
switch (key_type) {
case SSH_KEY_PRIVATE:
pkey = ENGINE_load_private_key(engine, uri_name, NULL, NULL);
if (pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not load key: %s",
- ERR_error_string(ERR_get_error(),NULL));
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
break;
case SSH_KEY_PUBLIC:
pkey = ENGINE_load_public_key(engine, uri_name, NULL, NULL);
if (pkey == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Could not load key: %s",
- ERR_error_string(ERR_get_error(),NULL));
+ ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
break;
default:
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Invalid key type: %d", key_type);
goto fail;
}
+#else /* WITH_PKCS11_PROVIDER */
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info = NULL;
+ int rv, expect_type = OSSL_STORE_INFO_PKEY;
+
+ /* The provider can be either configured in openssl.cnf or dynamically
+ * loaded, assuming it does not need any special configuration */
+ if (OSSL_PROVIDER_available(NULL, "pkcs11") == 0 &&
+ !pkcs11_provider_failed) {
+ OSSL_PROVIDER *pkcs11_provider = NULL;
+
+ pkcs11_provider = OSSL_PROVIDER_try_load(NULL, "pkcs11", 1);
+ if (pkcs11_provider == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to initialize provider: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ /* Do not attempt to load it again */
+ pkcs11_provider_failed = true;
+ goto fail;
+ }
+ }
+
+ store = OSSL_STORE_open(uri_name, NULL, NULL, NULL, NULL);
+ if (store == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to open OpenSSL store: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (key_type == SSH_KEY_PUBLIC) {
+ expect_type = OSSL_STORE_INFO_PUBKEY;
+ }
+ rv = OSSL_STORE_expect(store, expect_type);
+ if (rv != 1) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to set the store preference. Ignoring the error: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
+
+ for (info = OSSL_STORE_load(store);
+ info != NULL;
+ info = OSSL_STORE_load(store)) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if (ossl_type == OSSL_STORE_INFO_PUBKEY && key_type == SSH_KEY_PUBLIC) {
+ pkey = OSSL_STORE_INFO_get1_PUBKEY(info);
+ break;
+ } else if (ossl_type == OSSL_STORE_INFO_PKEY &&
+ key_type == SSH_KEY_PRIVATE) {
+ pkey = OSSL_STORE_INFO_get1_PKEY(info);
+ break;
+ } else {
+ SSH_LOG(SSH_LOG_TRACE,
+ "Ignoring object not matching our type: %d",
+ ossl_type);
+ }
+ }
+ OSSL_STORE_close(store);
+ if (pkey == NULL) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "No key found in the pkcs11 store: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+#endif /* WITH_PKCS11_PROVIDER */
key = ssh_key_new();
if (key == NULL) {
@@ -2617,20 +2844,14 @@ int pki_uri_import(const char *uri_name,
switch (EVP_PKEY_base_id(pkey)) {
case EVP_PKEY_RSA:
- rsa = EVP_PKEY_get1_RSA(pkey);
- if (rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
- "Parsing pub key: %s",
- ERR_error_string(ERR_get_error(),NULL));
- goto fail;
- }
type = SSH_KEYTYPE_RSA;
break;
case EVP_PKEY_EC:
#ifdef HAVE_OPENSSL_ECC
- ecdsa = EVP_PKEY_get1_EC_KEY(pkey);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ ecdsa = EVP_PKEY_get0_EC_KEY(pkey);
if (ecdsa == NULL) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Parsing pub key: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
@@ -2639,15 +2860,18 @@ int pki_uri_import(const char *uri_name,
/* pki_privatekey_type_from_string always returns P256 for ECDSA
* keys, so we need to figure out the correct type here */
type = pki_key_ecdsa_to_key_type(ecdsa);
+#else
+ type = pki_key_ecdsa_to_key_type(pkey);
+#endif /* OPENSSL_VERSION_NUMBER */
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid pub key.");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid pub key.");
goto fail;
}
break;
#endif
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid public key type %d",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid public key type %d",
EVP_PKEY_base_id(pkey));
goto fail;
}
@@ -2655,36 +2879,30 @@ int pki_uri_import(const char *uri_name,
key->key = pkey;
key->type = type;
key->type_c = ssh_key_type_to_char(type);
+ key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PKCS11_URI;
if (key_type == SSH_KEY_PRIVATE) {
- key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PKCS11_URI;
- } else {
- key->flags = SSH_KEY_FLAG_PUBLIC | SSH_KEY_FLAG_PKCS11_URI;
+ key->flags |= SSH_KEY_FLAG_PRIVATE;
}
- key->rsa = rsa;
- key->ecdsa = ecdsa;
#ifdef HAVE_OPENSSL_ECC
if (is_ecdsa_key_type(key->type)) {
- key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(ecdsa);
+#else
+ key->ecdsa_nid = pki_key_ecdsa_to_nid(key->key);
+#endif /* OPENSSL_VERSION_NUMBER */
}
#endif
*nkey = key;
- ENGINE_finish(engine);
- ENGINE_free(engine);
return SSH_OK;
fail:
- ENGINE_finish(engine);
- ENGINE_free(engine);
EVP_PKEY_free(pkey);
ssh_key_free(key);
- RSA_free(rsa);
-#ifdef HAVE_OPENSSL_ECC
- EC_KEY_free(ecdsa);
-#endif
return SSH_ERROR;
}
+#endif /* WITH_PKCS11_URI */
#endif /* _PKI_CRYPTO_H */
diff --git a/src/pki_ed25519.c b/src/pki_ed25519.c
index fdf94b4e..6a5a4a8a 100644
--- a/src/pki_ed25519.c
+++ b/src/pki_ed25519.c
@@ -1,5 +1,5 @@
/*
- * pki_ed25519 .c - PKI infrastructure using ed25519
+ * pki_ed25519.c - PKI infrastructure using ed25519
*
* This file is part of the SSH Library
*
diff --git a/src/pki_ed25519_common.c b/src/pki_ed25519_common.c
index 9db14dac..03859f7c 100644
--- a/src/pki_ed25519_common.c
+++ b/src/pki_ed25519_common.c
@@ -34,11 +34,11 @@ int pki_privkey_build_ed25519(ssh_key key,
if (ssh_string_len(pubkey) != ED25519_KEY_LEN ||
ssh_string_len(privkey) != (2 * ED25519_KEY_LEN))
{
- SSH_LOG(SSH_LOG_WARN, "Invalid ed25519 key len");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid ed25519 key len");
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* In OpenSSL implementation, the private key is the original private seed,
* without the public key. */
key->ed25519_privkey = malloc(ED25519_KEY_LEN);
@@ -56,7 +56,7 @@ int pki_privkey_build_ed25519(ssh_key key,
goto error;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
memcpy(key->ed25519_privkey, ssh_string_data(privkey),
ED25519_KEY_LEN);
#else
@@ -99,7 +99,7 @@ int pki_ed25519_key_cmp(const ssh_key k1,
if (k1->ed25519_privkey == NULL || k2->ed25519_privkey == NULL) {
return 1;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* In OpenSSL implementation, the private key is the original private
* seed, without the public key. */
cmp = memcmp(k1->ed25519_privkey, k2->ed25519_privkey, ED25519_KEY_LEN);
@@ -121,6 +121,10 @@ int pki_ed25519_key_cmp(const ssh_key k1,
if (cmp != 0) {
return 1;
}
+ break;
+ case SSH_KEY_CMP_CERTIFICATE:
+ /* handled globally */
+ return 1;
}
return 0;
@@ -137,39 +141,39 @@ int pki_ed25519_key_cmp(const ssh_key k1,
*
* @return SSH_ERROR on error, SSH_OK on success
*/
-int pki_ed25519_key_dup(ssh_key new, const ssh_key key)
+int pki_ed25519_key_dup(ssh_key new_key, const ssh_key key)
{
if (key->ed25519_privkey == NULL && key->ed25519_pubkey == NULL) {
return SSH_ERROR;
}
if (key->ed25519_privkey != NULL) {
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* In OpenSSL implementation, the private key is the original private
* seed, without the public key. */
- new->ed25519_privkey = malloc(ED25519_KEY_LEN);
+ new_key->ed25519_privkey = malloc(ED25519_KEY_LEN);
#else
/* In the internal implementation, the private key is the concatenation
* of the private seed with the public key. */
- new->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
+ new_key->ed25519_privkey = malloc(2 * ED25519_KEY_LEN);
#endif
- if (new->ed25519_privkey == NULL) {
+ if (new_key->ed25519_privkey == NULL) {
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_ED25519
- memcpy(new->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN);
+#ifdef HAVE_LIBCRYPTO
+ memcpy(new_key->ed25519_privkey, key->ed25519_privkey, ED25519_KEY_LEN);
#else
- memcpy(new->ed25519_privkey, key->ed25519_privkey, 2 * ED25519_KEY_LEN);
+ memcpy(new_key->ed25519_privkey, key->ed25519_privkey, 2 * ED25519_KEY_LEN);
#endif
}
if (key->ed25519_pubkey != NULL) {
- new->ed25519_pubkey = malloc(ED25519_KEY_LEN);
- if (new->ed25519_pubkey == NULL) {
- SAFE_FREE(new->ed25519_privkey);
+ new_key->ed25519_pubkey = malloc(ED25519_KEY_LEN);
+ if (new_key->ed25519_pubkey == NULL) {
+ SAFE_FREE(new_key->ed25519_privkey);
return SSH_ERROR;
}
- memcpy(new->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_LEN);
+ memcpy(new_key->ed25519_pubkey, key->ed25519_pubkey, ED25519_KEY_LEN);
}
return SSH_OK;
@@ -202,6 +206,34 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key)
return rc;
}
+/** @internal
+ * @brief exports a ed25519 private key to a string blob.
+ * @param[in] privkey private key to convert
+ * @param[out] buffer buffer to write the blob in.
+ * @returns SSH_OK on success
+ */
+int pki_ed25519_private_key_to_blob(ssh_buffer buffer, const ssh_key privkey)
+{
+ int rc;
+
+ if (privkey->type != SSH_KEYTYPE_ED25519) {
+ SSH_LOG(SSH_LOG_TRACE, "Type %s not supported", privkey->type_c);
+ return SSH_ERROR;
+ }
+ if (privkey->ed25519_privkey == NULL ||
+ privkey->ed25519_pubkey == NULL) {
+ return SSH_ERROR;
+ }
+ rc = ssh_buffer_pack(buffer,
+ "dPdPP",
+ (uint32_t)ED25519_KEY_LEN,
+ (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey,
+ (uint32_t)(2 * ED25519_KEY_LEN),
+ (size_t)ED25519_KEY_LEN, privkey->ed25519_privkey,
+ (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey);
+ return rc;
+}
+
/**
* @internal
*
@@ -214,8 +246,9 @@ int pki_ed25519_public_key_to_blob(ssh_buffer buffer, ssh_key key)
ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
{
ssh_string sig_blob;
+ int rc;
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
/* When using the OpenSSL implementation, the signature is stored in raw_sig
* which is shared by all algorithms.*/
if (sig->raw_sig == NULL) {
@@ -234,12 +267,16 @@ ssh_string pki_ed25519_signature_to_blob(ssh_signature sig)
return NULL;
}
-#ifdef HAVE_OPENSSL_ED25519
- ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig),
- ssh_string_len(sig->raw_sig));
+#ifdef HAVE_LIBCRYPTO
+ rc = ssh_string_fill(sig_blob, ssh_string_data(sig->raw_sig),
+ ssh_string_len(sig->raw_sig));
#else
- ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN);
+ rc = ssh_string_fill(sig_blob, sig->ed25519_sig, ED25519_SIG_LEN);
#endif
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
return sig_blob;
}
@@ -261,11 +298,11 @@ int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob)
len = ssh_string_len(sig_blob);
if (len != ED25519_SIG_LEN){
- SSH_LOG(SSH_LOG_WARN, "Invalid ssh-ed25519 signature len: %zu", len);
+ SSH_LOG(SSH_LOG_TRACE, "Invalid ssh-ed25519 signature len: %zu", len);
return SSH_ERROR;
}
-#ifdef HAVE_OPENSSL_ED25519
+#ifdef HAVE_LIBCRYPTO
sig->raw_sig = ssh_string_copy(sig_blob);
#else
sig->ed25519_sig = malloc(ED25519_SIG_LEN);
@@ -277,4 +314,3 @@ int pki_signature_from_ed25519_blob(ssh_signature sig, ssh_string sig_blob)
return SSH_OK;
}
-
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index 62ec8ea7..65bb77e6 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -45,8 +45,6 @@
#define MAXLINESIZE 80
#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----"
-#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
-#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----"
#define ECDSA_HEADER_BEGIN "-----BEGIN EC PRIVATE KEY-----"
#define ECDSA_HEADER_END "-----END EC PRIVATE KEY-----"
@@ -283,6 +281,20 @@ static int passphrase_to_key(char *data, unsigned int datalen,
return 0;
}
+void pki_key_clean(ssh_key key)
+{
+ if (key == NULL)
+ return;
+
+ if (key->rsa)
+ gcry_sexp_release(key->rsa);
+ if (key->ecdsa)
+ gcry_sexp_release(key->ecdsa);
+
+ key->rsa = NULL;
+ key->ecdsa = NULL;
+}
+
static int privatekey_decrypt(int algo, int mode, unsigned int key_len,
unsigned char *iv, unsigned int iv_len,
ssh_buffer data, ssh_auth_callback cb,
@@ -419,10 +431,6 @@ static ssh_buffer privatekey_string_to_buffer(const char *pkey, int type,
}
switch(type) {
- case SSH_KEYTYPE_DSS:
- header_begin = DSA_HEADER_BEGIN;
- header_end = DSA_HEADER_END;
- break;
case SSH_KEYTYPE_RSA:
header_begin = RSA_HEADER_BEGIN;
header_end = RSA_HEADER_END;
@@ -626,79 +634,6 @@ error:
return rc;
}
-static int b64decode_dsa_privatekey(const char *pkey, gcry_sexp_t *r, ssh_auth_callback cb,
- void *userdata, const char *desc) {
- const unsigned char *data;
- ssh_buffer buffer = NULL;
- ssh_string p = NULL;
- ssh_string q = NULL;
- ssh_string g = NULL;
- ssh_string y = NULL;
- ssh_string x = NULL;
- ssh_string v = NULL;
- int rc = 1;
-
- buffer = privatekey_string_to_buffer(pkey, SSH_KEYTYPE_DSS, cb, userdata, desc);
- if (buffer == NULL) {
- return 0;
- }
-
- if (!asn1_check_sequence(buffer)) {
- SSH_BUFFER_FREE(buffer);
- return 0;
- }
-
- v = asn1_get_int(buffer);
- if (v == NULL) {
- SSH_BUFFER_FREE(buffer);
- return 0;
- }
-
- data = ssh_string_data(v);
- if (ssh_string_len(v) != 1 || data[0] != 0) {
- SSH_STRING_FREE(v);
- SSH_BUFFER_FREE(buffer);
- return 0;
- }
-
- p = asn1_get_int(buffer);
- q = asn1_get_int(buffer);
- g = asn1_get_int(buffer);
- y = asn1_get_int(buffer);
- x = asn1_get_int(buffer);
- SSH_BUFFER_FREE(buffer);
-
- if (p == NULL || q == NULL || g == NULL || y == NULL || x == NULL) {
- rc = 0;
- goto error;
- }
-
- if (gcry_sexp_build(r, NULL,
- "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))",
- ssh_string_len(p), ssh_string_data(p),
- ssh_string_len(q), ssh_string_data(q),
- ssh_string_len(g), ssh_string_data(g),
- ssh_string_len(y), ssh_string_data(y),
- ssh_string_len(x), ssh_string_data(x))) {
- rc = 0;
- }
-
-error:
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(y);
- SSH_STRING_FREE(y);
- ssh_string_burn(x);
- SSH_STRING_FREE(x);
- SSH_STRING_FREE(v);
-
- return rc;
-}
-
#ifdef HAVE_GCRYPT_ECC
static int pki_key_ecdsa_to_nid(gcry_sexp_t k)
{
@@ -938,7 +873,7 @@ ssh_string pki_private_key_to_pem(const ssh_key key,
(void) auth_fn;
(void) auth_data;
- SSH_LOG(SSH_LOG_WARN, "PEM export not supported by gcrypt backend!");
+ SSH_LOG(SSH_LOG_TRACE, "PEM export not supported by gcrypt backend!");
return NULL;
}
@@ -948,7 +883,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
ssh_auth_callback auth_fn,
void *auth_data)
{
- gcry_sexp_t dsa = NULL;
gcry_sexp_t rsa = NULL;
gcry_sexp_t ecdsa = NULL;
ssh_key key = NULL;
@@ -957,30 +891,11 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
type = pki_privatekey_type_from_string(b64_key);
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key.");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key.");
return NULL;
}
switch (type) {
- case SSH_KEYTYPE_DSS:
- if (passphrase == NULL) {
- if (auth_fn) {
- valid = b64decode_dsa_privatekey(b64_key, &dsa, auth_fn,
- auth_data, "Passphrase for private key:");
- } else {
- valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, NULL,
- NULL);
- }
- } else {
- valid = b64decode_dsa_privatekey(b64_key, &dsa, NULL, (void *)
- passphrase, NULL);
- }
-
- if (!valid) {
- SSH_LOG(SSH_LOG_WARN, "Parsing private key");
- goto fail;
- }
- break;
case SSH_KEYTYPE_RSA:
if (passphrase == NULL) {
if (auth_fn) {
@@ -996,7 +911,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
}
if (!valid) {
- SSH_LOG(SSH_LOG_WARN, "Parsing private key");
+ SSH_LOG(SSH_LOG_TRACE, "Error parsing private key");
goto fail;
}
break;
@@ -1027,7 +942,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
}
if (!valid) {
- SSH_LOG(SSH_LOG_WARN, "Parsing private key");
+ SSH_LOG(SSH_LOG_TRACE, "Error parsing private key");
goto fail;
}
@@ -1035,7 +950,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
* keys, so we need to figure out the correct type here */
type = pki_key_ecdsa_to_key_type(ecdsa);
if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid private key.");
+ SSH_LOG(SSH_LOG_TRACE, "Invalid private key.");
goto fail;
}
break;
@@ -1045,7 +960,7 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d", type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown or invalid private key type %d", type);
return NULL;
}
@@ -1057,7 +972,6 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
key->type = type;
key->type_c = ssh_key_type_to_char(type);
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- key->dsa = dsa;
key->rsa = rsa;
key->ecdsa = ecdsa;
#ifdef HAVE_GCRYPT_ECC
@@ -1069,52 +983,12 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
return key;
fail:
ssh_key_free(key);
- gcry_sexp_release(dsa);
gcry_sexp_release(rsa);
gcry_sexp_release(ecdsa);
return NULL;
}
-int pki_privkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey,
- ssh_string privkey)
-{
- gcry_sexp_build(&key->dsa, NULL,
- "(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))",
- ssh_string_len(p), ssh_string_data(p),
- ssh_string_len(q), ssh_string_data(q),
- ssh_string_len(g), ssh_string_data(g),
- ssh_string_len(pubkey), ssh_string_data(pubkey),
- ssh_string_len(privkey), ssh_string_data(privkey));
- if (key->dsa == NULL) {
- return SSH_ERROR;
- }
-
- return SSH_OK;
-}
-
-int pki_pubkey_build_dss(ssh_key key,
- ssh_string p,
- ssh_string q,
- ssh_string g,
- ssh_string pubkey) {
- gcry_sexp_build(&key->dsa, NULL,
- "(public-key(dsa(p %b)(q %b)(g %b)(y %b)))",
- ssh_string_len(p), ssh_string_data(p),
- ssh_string_len(q), ssh_string_data(q),
- ssh_string_len(g), ssh_string_data(g),
- ssh_string_len(pubkey), ssh_string_data(pubkey));
- if (key->dsa == NULL) {
- return SSH_ERROR;
- }
-
- return SSH_OK;
-}
-
int pki_privkey_build_rsa(ssh_key key,
ssh_string n,
ssh_string e,
@@ -1226,32 +1100,6 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
switch(key->type) {
- case SSH_KEYTYPE_DSS:
- err = gcry_sexp_extract_param(key->dsa,
- NULL,
- "pqgyx?",
- &p,
- &q,
- &g,
- &y,
- &x,
- NULL);
- if (err != 0) {
- break;
- }
-
- if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
- err = gcry_sexp_build(&new->dsa,
- NULL,
- "(private-key(dsa(p %m)(q %m)(g %m)(y %m)(x %m)))",
- p, q, g, y, x);
- } else {
- err = gcry_sexp_build(&new->dsa,
- NULL,
- "(public-key(dsa(p %m)(q %m)(g %m)(y %m)))",
- p, q, g, y);
- }
- break;
case SSH_KEYTYPE_RSA:
err = gcry_sexp_extract_param(key->rsa,
NULL,
@@ -1353,9 +1201,9 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
}
static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int type){
- gcry_sexp_t parms;
+ gcry_sexp_t params;
int rc;
- rc = gcry_sexp_build(&parms,
+ rc = gcry_sexp_build(&params,
NULL,
"(genkey(%s(nbits %d)(transient-key)))",
type_s,
@@ -1364,20 +1212,17 @@ static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int
return SSH_ERROR;
switch (type) {
case SSH_KEYTYPE_RSA:
- rc = gcry_pk_genkey(&key->rsa, parms);
- break;
- case SSH_KEYTYPE_DSS:
- rc = gcry_pk_genkey(&key->dsa, parms);
+ rc = gcry_pk_genkey(&key->rsa, params);
break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
- rc = gcry_pk_genkey(&key->ecdsa, parms);
+ rc = gcry_pk_genkey(&key->ecdsa, params);
break;
default:
assert (! "reached");
}
- gcry_sexp_release(parms);
+ gcry_sexp_release(params);
if (rc != 0)
return SSH_ERROR;
return SSH_OK;
@@ -1386,9 +1231,6 @@ static int pki_key_generate(ssh_key key, int parameter, const char *type_s, int
int pki_key_generate_rsa(ssh_key key, int parameter){
return pki_key_generate(key, parameter, "rsa", SSH_KEYTYPE_RSA);
}
-int pki_key_generate_dss(ssh_key key, int parameter){
- return pki_key_generate(key, parameter, "dsa", SSH_KEYTYPE_DSS);
-}
#ifdef HAVE_GCRYPT_ECC
int pki_key_generate_ecdsa(ssh_key key, int parameter) {
@@ -1455,30 +1297,8 @@ int pki_key_compare(const ssh_key k1,
enum ssh_keycmp_e what)
{
switch (k1->type) {
- case SSH_KEYTYPE_DSS:
- if (_bignum_cmp(k1->dsa, k2->dsa, "p") != 0) {
- return 1;
- }
-
- if (_bignum_cmp(k1->dsa, k2->dsa, "q") != 0) {
- return 1;
- }
-
- if (_bignum_cmp(k1->dsa, k2->dsa, "g") != 0) {
- return 1;
- }
-
- if (_bignum_cmp(k1->dsa, k2->dsa, "y") != 0) {
- return 1;
- }
-
- if (what == SSH_KEY_CMP_PRIVATE) {
- if (_bignum_cmp(k1->dsa, k2->dsa, "x") != 0) {
- return 1;
- }
- }
- break;
case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) {
return 1;
}
@@ -1506,13 +1326,19 @@ int pki_key_compare(const ssh_key k1,
}
break;
case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
case SSH_KEYTYPE_SK_ED25519:
- /* ed25519 keys handled globaly */
- return 0;
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys handled globally */
+ return 0;
case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
#ifdef HAVE_GCRYPT_ECC
if (k1->ecdsa_nid != k2->ecdsa_nid) {
return 1;
@@ -1529,15 +1355,9 @@ int pki_key_compare(const ssh_key k1,
}
break;
#endif
- case SSH_KEYTYPE_DSS_CERT01:
- case SSH_KEYTYPE_RSA_CERT01:
- case SSH_KEYTYPE_ECDSA:
- case SSH_KEYTYPE_ECDSA_P256_CERT01:
- case SSH_KEYTYPE_ECDSA_P384_CERT01:
- case SSH_KEYTYPE_ECDSA_P521_CERT01:
- case SSH_KEYTYPE_SK_ECDSA_CERT01:
- case SSH_KEYTYPE_ED25519_CERT01:
- case SSH_KEYTYPE_SK_ED25519_CERT01:
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_ECDSA: /* deprecated */
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
return 1;
@@ -1546,16 +1366,18 @@ int pki_key_compare(const ssh_key k1,
return 0;
}
-ssh_string pki_publickey_to_blob(const ssh_key key)
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
{
ssh_buffer buffer;
ssh_string type_s;
ssh_string str = NULL;
ssh_string e = NULL;
ssh_string n = NULL;
+ ssh_string d = NULL;
ssh_string p = NULL;
ssh_string g = NULL;
ssh_string q = NULL;
+ ssh_string u = NULL;
int rc;
buffer = ssh_buffer_new();
@@ -1586,66 +1408,6 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
}
switch (key->type) {
- case SSH_KEYTYPE_DSS:
- p = ssh_sexp_extract_mpi(key->dsa,
- "p",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (p == NULL) {
- goto fail;
- }
-
- q = ssh_sexp_extract_mpi(key->dsa,
- "q",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (q == NULL) {
- goto fail;
- }
-
- g = ssh_sexp_extract_mpi(key->dsa,
- "g",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (g == NULL) {
- goto fail;
- }
-
- n = ssh_sexp_extract_mpi(key->dsa,
- "y",
- GCRYMPI_FMT_USG,
- GCRYMPI_FMT_STD);
- if (n == NULL) {
- goto fail;
- }
-
- rc = ssh_buffer_add_ssh_string(buffer, p);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, q);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, g);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, n);
- if (rc < 0) {
- goto fail;
- }
-
- ssh_string_burn(p);
- SSH_STRING_FREE(p);
- ssh_string_burn(g);
- SSH_STRING_FREE(g);
- ssh_string_burn(q);
- SSH_STRING_FREE(q);
- ssh_string_burn(n);
- SSH_STRING_FREE(n);
-
- break;
case SSH_KEYTYPE_RSA:
e = ssh_sexp_extract_mpi(key->rsa,
"e",
@@ -1663,30 +1425,108 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
goto fail;
}
- rc = ssh_buffer_add_ssh_string(buffer, e);
- if (rc < 0) {
- goto fail;
- }
- rc = ssh_buffer_add_ssh_string(buffer, n);
- if (rc < 0) {
- goto fail;
- }
+ if (type == SSH_KEY_PUBLIC) {
+ /* The N and E parts are swapped in the public key export ! */
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ } else if (type == SSH_KEY_PRIVATE) {
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ d = ssh_sexp_extract_mpi(key->rsa,
+ "d",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ p = ssh_sexp_extract_mpi(key->rsa,
+ "p",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (p == NULL) {
+ goto fail;
+ }
+ q = ssh_sexp_extract_mpi(key->rsa,
+ "q",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ u = ssh_sexp_extract_mpi(key->rsa,
+ "u",
+ GCRYMPI_FMT_USG,
+ GCRYMPI_FMT_STD);
+ if (u == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, u);
+ if (rc < 0) {
+ goto fail;
+ }
+ /* Swap the P and Q as the iqmp in gcrypt is ipmq ... */
+ rc = ssh_buffer_add_ssh_string(buffer, q);
+ if (rc < 0) {
+ goto fail;
+ }
+ rc = ssh_buffer_add_ssh_string(buffer, p);
+ if (rc < 0) {
+ goto fail;
+ }
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ ssh_string_burn(p);
+ SSH_STRING_FREE(p);
+ ssh_string_burn(q);
+ SSH_STRING_FREE(q);
+ ssh_string_burn(u);
+ SSH_STRING_FREE(u);
+ }
ssh_string_burn(e);
SSH_STRING_FREE(e);
ssh_string_burn(n);
SSH_STRING_FREE(n);
-
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- rc = pki_ed25519_public_key_to_blob(buffer, key);
- if (rc != SSH_OK){
- goto fail;
- }
- if (key->type == SSH_KEYTYPE_SK_ED25519 &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PUBLIC) {
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
+ /* public key can contain certificate sk information */
+ if (key->type == SSH_KEYTYPE_SK_ED25519) {
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+ } else {
+ rc = pki_ed25519_private_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
}
break;
case SSH_KEYTYPE_ECDSA_P256:
@@ -1697,22 +1537,19 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
type_s = ssh_string_from_char(
pki_key_ecdsa_nid_to_char(key->ecdsa_nid));
if (type_s == NULL) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
+ goto fail;
}
rc = ssh_buffer_add_ssh_string(buffer, type_s);
SSH_STRING_FREE(type_s);
if (rc < 0) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
+ goto fail;
}
e = ssh_sexp_extract_mpi(key->ecdsa, "q", GCRYMPI_FMT_STD,
GCRYMPI_FMT_STD);
if (e == NULL) {
- SSH_BUFFER_FREE(buffer);
- return NULL;
+ goto fail;
}
rc = ssh_buffer_add_ssh_string(buffer, e);
@@ -1724,9 +1561,27 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
SSH_STRING_FREE(e);
e = NULL;
- if (key->type == SSH_KEYTYPE_SK_ECDSA &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PRIVATE) {
+ d = ssh_sexp_extract_mpi(key->ecdsa, "d", GCRYMPI_FMT_STD,
+ GCRYMPI_FMT_STD);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ } else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
+ /* public key can contain certificate sk information */
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
}
break;
@@ -1770,66 +1625,14 @@ fail:
ssh_string pki_signature_to_blob(const ssh_signature sig)
{
- char buffer[40] = { 0 };
-
- const char *r = NULL;
- size_t r_len, r_offset_in, r_offset_out;
-
- const char *s = NULL;
- size_t s_len, s_offset_in, s_offset_out;
+ const char *s = NULL; /* used in RSA */
gcry_sexp_t sexp;
size_t size = 0;
ssh_string sig_blob = NULL;
+ int rc;
switch(sig->type) {
- case SSH_KEYTYPE_DSS:
- sexp = gcry_sexp_find_token(sig->dsa_sig, "r", 0);
- if (sexp == NULL) {
- return NULL;
- }
- r = gcry_sexp_nth_data(sexp, 1, &size);
- /* libgcrypt put 0 when first bit is set */
- if (*r == 0) {
- size--;
- r++;
- }
-
- r_len = size;
- r_offset_in = (r_len > 20) ? (r_len - 20) : 0;
- r_offset_out = (r_len < 20) ? (20 - r_len) : 0;
- memcpy(buffer + r_offset_out,
- r + r_offset_in,
- r_len - r_offset_in);
-
- gcry_sexp_release(sexp);
-
- sexp = gcry_sexp_find_token(sig->dsa_sig, "s", 0);
- if (sexp == NULL) {
- return NULL;
- }
- s = gcry_sexp_nth_data(sexp,1,&size);
- if (*s == 0) {
- size--;
- s++;
- }
-
- s_len = size;
- s_offset_in = (s_len > 20) ? (s_len - 20) : 0;
- s_offset_out = (s_len < 20) ? (20 - s_len) : 0;
- memcpy(buffer + 20 + s_offset_out,
- s + s_offset_in,
- s_len - s_offset_in);
-
- gcry_sexp_release(sexp);
-
- sig_blob = ssh_string_new(40);
- if (sig_blob == NULL) {
- return NULL;
- }
-
- ssh_string_fill(sig_blob, buffer, 40);
- break;
case SSH_KEYTYPE_RSA:
sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0);
if (sexp == NULL) {
@@ -1845,13 +1648,16 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
if (sig_blob == NULL) {
return NULL;
}
- ssh_string_fill(sig_blob, discard_const_p(char, s), size);
-
+ rc = ssh_string_fill(sig_blob, discard_const_p(char, s), size);
gcry_sexp_release(sexp);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
break;
case SSH_KEYTYPE_ED25519:
- sig_blob = pki_ed25519_signature_to_blob(sig);
- break;
+ sig_blob = pki_ed25519_signature_to_blob(sig);
+ break;
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
@@ -1860,7 +1666,6 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
ssh_string R;
ssh_string S;
ssh_buffer b;
- int rc;
b = ssh_buffer_new();
if (b == NULL) {
@@ -1901,16 +1706,20 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
return NULL;
}
- ssh_string_fill(sig_blob,
+ rc = ssh_string_fill(sig_blob,
ssh_buffer_get(b), ssh_buffer_get_len(b));
SSH_BUFFER_FREE(b);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
break;
}
#endif
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %d", sig->type);
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %d", sig->type);
return NULL;
break;
}
@@ -1930,7 +1739,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
int rc;
if (ssh_key_type_plain(pubkey->type) != type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Incompatible public key provided (%d) expecting (%d)",
type,
pubkey->type);
@@ -1949,42 +1758,14 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
len = ssh_string_len(sig_blob);
switch(type) {
- case SSH_KEYTYPE_DSS:
- /* 40 is the dual signature blob len. */
- if (len != 40) {
- SSH_LOG(SSH_LOG_WARN,
- "Signature has wrong size: %lu",
- (unsigned long)len);
- ssh_signature_free(sig);
- return NULL;
- }
-
-#ifdef DEBUG_CRYPTO
- SSH_LOG(SSH_LOG_DEBUG,
- "DSA signature len: %lu",
- (unsigned long)len);
- ssh_log_hexdump("DSA signature", ssh_string_data(sig_blob), len);
-#endif
-
- err = gcry_sexp_build(&sig->dsa_sig,
- NULL,
- "(sig-val(dsa(r %b)(s %b)))",
- 20,
- ssh_string_data(sig_blob),
- 20,
- (unsigned char *)ssh_string_data(sig_blob) + 20);
- if (err) {
- ssh_signature_free(sig);
- return NULL;
- }
- break;
case SSH_KEYTYPE_RSA:
rsalen = (gcry_pk_get_nbits(pubkey->rsa) + 7) / 8;
if (len > rsalen) {
- SSH_LOG(SSH_LOG_WARN,
- "Signature is to big size: %lu",
- (unsigned long)len);
+ SSH_LOG(SSH_LOG_TRACE,
+ "Signature is too big: %lu > %lu",
+ (unsigned long)len,
+ (unsigned long)rsalen);
ssh_signature_free(sig);
return NULL;
}
@@ -2062,7 +1843,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
}
if (rlen != 0) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature has remaining bytes in inner "
"sigblob: %lu",
(unsigned long)rlen);
@@ -2100,7 +1881,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
case SSH_KEYTYPE_RSA1:
case SSH_KEYTYPE_UNKNOWN:
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature type");
return NULL;
}
@@ -2112,7 +1893,6 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
size_t hlen,
enum ssh_digest_e hash_type)
{
- unsigned char ghash[hlen + 1];
const char *hash_c = NULL;
ssh_signature sig;
gcry_sexp_t sexp;
@@ -2126,28 +1906,6 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
sig->type_c = ssh_key_signature_to_char(privkey->type, hash_type);
sig->hash_type = hash_type;
switch (privkey->type) {
- case SSH_KEYTYPE_DSS:
- /* That is to mark the number as positive */
- if(hash[0] >= 0x80) {
- memcpy(ghash + 1, hash, hlen);
- ghash[0] = 0;
- hash = ghash;
- hlen += 1;
- }
-
- err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash);
- if (err) {
- ssh_signature_free(sig);
- return NULL;
- }
-
- err = gcry_pk_sign(&sig->dsa_sig, sexp, privkey->dsa);
- gcry_sexp_release(sexp);
- if (err) {
- ssh_signature_free(sig);
- return NULL;
- }
- break;
case SSH_KEYTYPE_RSA:
switch (hash_type) {
case SSH_DIGEST_SHA1:
@@ -2161,7 +1919,7 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
break;
case SSH_DIGEST_AUTO:
default:
- SSH_LOG(SSH_LOG_WARN, "Incompatible key algorithm");
+ SSH_LOG(SSH_LOG_TRACE, "Incompatible key algorithm");
return NULL;
}
err = gcry_sexp_build(&sexp,
@@ -2380,32 +2138,6 @@ int pki_verify_data_signature(ssh_signature signature,
}
switch(pubkey->type) {
- case SSH_KEYTYPE_DSS:
- case SSH_KEYTYPE_DSS_CERT01:
- /* That is to mark the number as positive */
- if(hash[0] >= 0x80) {
- hash = ghash;
- hlen += 1;
- }
-
- err = gcry_sexp_build(&sexp, NULL, "%b", hlen, hash);
- if (err) {
- SSH_LOG(SSH_LOG_TRACE,
- "DSA hash error: %s", gcry_strerror(err));
- return SSH_ERROR;
- }
- err = gcry_pk_verify(signature->dsa_sig, sexp, pubkey->dsa);
- gcry_sexp_release(sexp);
- if (err) {
- SSH_LOG(SSH_LOG_TRACE, "Invalid DSA signature");
- if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) {
- SSH_LOG(SSH_LOG_TRACE,
- "DSA verify error: %s",
- gcry_strerror(err));
- }
- return SSH_ERROR;
- }
- break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA_CERT01:
err = gcry_sexp_build(&sexp,
@@ -2483,13 +2215,45 @@ int pki_verify_data_signature(ssh_signature signature,
return SSH_OK;
}
+int ssh_key_size(ssh_key key)
+{
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ return gcry_pk_get_nbits(key->rsa);
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ return gcry_pk_get_nbits(key->ecdsa);
+ case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys have fixed size */
+ return 255;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_UNKNOWN:
+ default:
+ return SSH_ERROR;
+ }
+}
+
+#ifdef WITH_PKCS11_URI
int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type)
{
(void) uri_name;
(void) key;
(void) key_type;
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"gcrypt does not support PKCS #11");
return SSH_ERROR;
}
+#endif /* WITH_PKCS11_URI */
#endif /* HAVE_LIBGCRYPT */
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index cac357f8..962ae1fe 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -26,6 +26,7 @@
#ifdef HAVE_LIBMBEDCRYPTO
#include <mbedtls/pk.h>
#include <mbedtls/error.h>
+#include "mbedcrypto-compat.h"
#include "libssh/priv.h"
#include "libssh/pki.h"
@@ -37,6 +38,22 @@
#define MAX_PASSPHRASE_SIZE 1024
#define MAX_KEY_SIZE 32
+void pki_key_clean(ssh_key key)
+{
+ if (key == NULL)
+ return;
+
+ if (key->rsa != NULL) {
+ mbedtls_pk_free(key->rsa);
+ SAFE_FREE(key->rsa);
+ }
+
+ if (key->ecdsa != NULL) {
+ mbedtls_ecdsa_free(key->ecdsa);
+ SAFE_FREE(key->ecdsa);
+ }
+}
+
ssh_string pki_private_key_to_pem(const ssh_key key, const char *passphrase,
ssh_auth_callback auth_fn, void *auth_data)
{
@@ -50,7 +67,7 @@ static int pki_key_ecdsa_to_nid(mbedtls_ecdsa_context *ecdsa)
{
mbedtls_ecp_group_id id;
- id = ecdsa->grp.id;
+ id = ecdsa->MBEDTLS_PRIVATE(grp.id);
if (id == MBEDTLS_ECP_DP_SECP256R1) {
return NID_mbedtls_nistp256;
} else if (id == MBEDTLS_ECP_DP_SECP384R1) {
@@ -84,118 +101,110 @@ ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase,
ssh_auth_callback auth_fn, void *auth_data)
{
ssh_key key = NULL;
- mbedtls_pk_context *rsa = NULL;
- mbedtls_pk_context *ecdsa = NULL;
- ed25519_privkey *ed25519 = NULL;
- enum ssh_keytypes_e type;
+ mbedtls_pk_context *pk = NULL;
+ mbedtls_pk_type_t mbed_type;
int valid;
/* mbedtls pk_parse_key expects strlen to count the 0 byte */
size_t b64len = strlen(b64_key) + 1;
unsigned char tmp[MAX_PASSPHRASE_SIZE] = {0};
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_ctr_drbg_context *ctr_drbg = ssh_get_mbedtls_ctr_drbg_context();
+#endif
- type = pki_privatekey_type_from_string(b64_key);
- if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key.");
- return NULL;
+ pk = malloc(sizeof(mbedtls_pk_context));
+ if (pk == NULL) {
+ goto fail;
}
-
- switch (type) {
- case SSH_KEYTYPE_RSA:
- rsa = malloc(sizeof(mbedtls_pk_context));
- if (rsa == NULL) {
- return NULL;
- }
-
- mbedtls_pk_init(rsa);
-
- if (passphrase == NULL) {
- if (auth_fn) {
- valid = auth_fn("Passphrase for private key:", (char *) tmp,
- MAX_PASSPHRASE_SIZE, 0, 0, auth_data);
- if (valid < 0) {
- goto fail;
- }
- /* TODO fix signedness and strlen */
- valid = mbedtls_pk_parse_key(rsa,
- (const unsigned char *) b64_key,
- b64len, tmp,
- strnlen((const char *) tmp, MAX_PASSPHRASE_SIZE));
- } else {
- valid = mbedtls_pk_parse_key(rsa,
- (const unsigned char *) b64_key,
- b64len, NULL,
- 0);
- }
- } else {
- valid = mbedtls_pk_parse_key(rsa,
- (const unsigned char *) b64_key, b64len,
- (const unsigned char *) passphrase,
- strnlen(passphrase, MAX_PASSPHRASE_SIZE));
- }
-
- if (valid != 0) {
- char error_buf[100];
- mbedtls_strerror(valid, error_buf, 100);
- SSH_LOG(SSH_LOG_WARN,"Parsing private key %s", error_buf);
+ mbedtls_pk_init(pk);
+
+ if (passphrase == NULL) {
+ if (auth_fn) {
+ valid = auth_fn("Passphrase for private key:",
+ (char *)tmp,
+ MAX_PASSPHRASE_SIZE,
+ 0,
+ 0,
+ auth_data);
+ if (valid < 0) {
goto fail;
}
- break;
- case SSH_KEYTYPE_ECDSA_P256:
- case SSH_KEYTYPE_ECDSA_P384:
- case SSH_KEYTYPE_ECDSA_P521:
- ecdsa = malloc(sizeof(mbedtls_pk_context));
- if (ecdsa == NULL) {
- return NULL;
- }
-
- mbedtls_pk_init(ecdsa);
-
- if (passphrase == NULL) {
- if (auth_fn) {
- valid = auth_fn("Passphrase for private key:", (char *) tmp,
- MAX_PASSPHRASE_SIZE, 0, 0, auth_data);
- if (valid < 0) {
- goto fail;
- }
- valid = mbedtls_pk_parse_key(ecdsa,
- (const unsigned char *) b64_key,
- b64len, tmp,
- strnlen((const char *) tmp, MAX_PASSPHRASE_SIZE));
- } else {
- valid = mbedtls_pk_parse_key(ecdsa,
- (const unsigned char *) b64_key,
- b64len, NULL,
- 0);
- }
- } else {
- valid = mbedtls_pk_parse_key(ecdsa,
- (const unsigned char *) b64_key, b64len,
- (const unsigned char *) passphrase,
- strnlen(passphrase, MAX_PASSPHRASE_SIZE));
- }
-
- if (valid != 0) {
- char error_buf[100];
- mbedtls_strerror(valid, error_buf, 100);
- SSH_LOG(SSH_LOG_WARN,"Parsing private key %s", error_buf);
- goto fail;
- }
- break;
- case SSH_KEYTYPE_ED25519:
- /* Cannot open ed25519 keys with libmbedcrypto */
- default:
- SSH_LOG(SSH_LOG_WARN, "Unknown or invalid private key type %d",
- type);
- return NULL;
+#if MBEDTLS_VERSION_MAJOR > 2
+ valid = mbedtls_pk_parse_key(
+ pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ tmp,
+ strnlen((const char *)tmp, MAX_PASSPHRASE_SIZE),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+#else
+ valid = mbedtls_pk_parse_key(
+ pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ tmp,
+ strnlen((const char *)tmp, MAX_PASSPHRASE_SIZE));
+#endif
+ } else {
+#if MBEDTLS_VERSION_MAJOR > 2
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ NULL,
+ 0,
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+#else
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ NULL,
+ 0);
+#endif
+ }
+ } else {
+#if MBEDTLS_VERSION_MAJOR > 2
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ (const unsigned char *)passphrase,
+ strnlen(passphrase, MAX_PASSPHRASE_SIZE),
+ mbedtls_ctr_drbg_random,
+ ctr_drbg);
+#else
+ valid = mbedtls_pk_parse_key(pk,
+ (const unsigned char *)b64_key,
+ b64len,
+ (const unsigned char *)passphrase,
+ strnlen(passphrase, MAX_PASSPHRASE_SIZE));
+#endif
}
+ if (valid != 0) {
+ char error_buf[100];
+ mbedtls_strerror(valid, error_buf, 100);
+ SSH_LOG(SSH_LOG_WARN, "Parsing private key %s", error_buf);
+ goto fail;
+ }
+
+ mbed_type = mbedtls_pk_get_type(pk);
key = ssh_key_new();
if (key == NULL) {
goto fail;
}
- if (ecdsa != NULL) {
- mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(*ecdsa);
+ switch (mbed_type) {
+ case MBEDTLS_PK_RSA:
+ case MBEDTLS_PK_RSA_ALT:
+ key->rsa = pk;
+ pk = NULL;
+ key->type = SSH_KEYTYPE_RSA;
+ break;
+ case MBEDTLS_PK_ECKEY:
+ case MBEDTLS_PK_ECDSA: {
+ /* type will be set later */
+ mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(*pk);
+ pk = NULL;
key->ecdsa = malloc(sizeof(mbedtls_ecdsa_context));
if (key->ecdsa == NULL) {
@@ -204,40 +213,36 @@ ssh_key pki_private_key_from_base64(const char *b64_key, const char *passphrase,
mbedtls_ecdsa_init(key->ecdsa);
mbedtls_ecdsa_from_keypair(key->ecdsa, keypair);
- mbedtls_pk_free(ecdsa);
- SAFE_FREE(ecdsa);
+ mbedtls_pk_free(pk);
+ SAFE_FREE(pk);
key->ecdsa_nid = pki_key_ecdsa_to_nid(key->ecdsa);
/* pki_privatekey_type_from_string always returns P256 for ECDSA
- * keys, so we need to figure out the correct type here */
- type = pki_key_ecdsa_to_key_type(key->ecdsa);
- if (type == SSH_KEYTYPE_UNKNOWN) {
- SSH_LOG(SSH_LOG_WARN, "Invalid private key.");
+ * keys, so we need to figure out the correct type here */
+ key->type = pki_key_ecdsa_to_key_type(key->ecdsa);
+ if (key->type == SSH_KEYTYPE_UNKNOWN) {
+ SSH_LOG(SSH_LOG_TRACE, "Invalid private key.");
goto fail;
}
- } else {
- key->ecdsa = NULL;
+ break;
+ }
+ default:
+ SSH_LOG(SSH_LOG_WARN,
+ "Unknown or invalid private key type %d",
+ mbed_type);
+ return NULL;
}
- key->type = type;
- key->type_c = ssh_key_type_to_char(type);
+ key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
- key->rsa = rsa;
- key->ed25519_privkey = ed25519;
- rsa = NULL;
- ecdsa = NULL;
return key;
fail:
ssh_key_free(key);
- if (rsa != NULL) {
- mbedtls_pk_free(rsa);
- SAFE_FREE(rsa);
- }
- if (ecdsa != NULL) {
- mbedtls_pk_free(ecdsa);
- SAFE_FREE(ecdsa);
+ if (pk != NULL) {
+ mbedtls_pk_free(pk);
+ SAFE_FREE(pk);
}
return NULL;
}
@@ -276,19 +281,19 @@ int pki_privkey_build_rsa(ssh_key key,
ssh_string_data(d), ssh_string_len(d),
ssh_string_data(e), ssh_string_len(e));
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to import private RSA key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to import private RSA key");
goto fail;
}
rc = mbedtls_rsa_complete(rsa);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN, "Failed to complete private RSA key");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to complete private RSA key");
goto fail;
}
rc = mbedtls_rsa_check_privkey(rsa);
if (rc != 0) {
- SSH_LOG(SSH_LOG_WARN, "Inconsistent private RSA key");
+ SSH_LOG(SSH_LOG_TRACE, "Inconsistent private RSA key");
goto fail;
}
@@ -304,6 +309,10 @@ int pki_pubkey_build_rsa(ssh_key key, ssh_string e, ssh_string n)
{
mbedtls_rsa_context *rsa = NULL;
const mbedtls_pk_info_t *pk_info = NULL;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi N;
+ mbedtls_mpi E;
+#endif
int rc;
key->rsa = malloc(sizeof(mbedtls_pk_context));
@@ -320,26 +329,59 @@ int pki_pubkey_build_rsa(ssh_key key, ssh_string e, ssh_string n)
goto fail;
}
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_init(&N);
+ mbedtls_mpi_init(&E);
+#endif
+
rsa = mbedtls_pk_rsa(*key->rsa);
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_mpi_read_binary(&N, ssh_string_data(n),
+ ssh_string_len(n));
+#else
rc = mbedtls_mpi_read_binary(&rsa->N, ssh_string_data(n),
ssh_string_len(n));
+#endif
if (rc != 0) {
goto fail;
}
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_mpi_read_binary(&E, ssh_string_data(e),
+ ssh_string_len(e));
+#else
rc = mbedtls_mpi_read_binary(&rsa->E, ssh_string_data(e),
ssh_string_len(e));
+#endif
if (rc != 0) {
goto fail;
}
- rsa->len = (mbedtls_mpi_bitlen(&rsa->N) + 7) >> 3;
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_import(rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
- return SSH_OK;
+ rc = mbedtls_rsa_complete(rsa);
+ if (rc != 0) {
+ goto fail;
+ }
+#else
+ rsa->len = (mbedtls_mpi_bitlen(&rsa->N) + 7) >> 3;
+#endif
+ rc = SSH_OK;
+ goto exit;
fail:
+ rc = SSH_ERROR;
mbedtls_pk_free(key->rsa);
SAFE_FREE(key->rsa);
- return SSH_ERROR;
+exit:
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+#endif
+ return rc;
}
ssh_key pki_key_dup(const ssh_key key, int demote)
@@ -347,7 +389,13 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
ssh_key new = NULL;
int rc;
const mbedtls_pk_info_t *pk_info = NULL;
-
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi N;
+ mbedtls_mpi E;
+ mbedtls_mpi D;
+ mbedtls_mpi P;
+ mbedtls_mpi Q;
+#endif
new = ssh_key_new();
if (new == NULL) {
@@ -362,6 +410,13 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
new->flags = key->flags;
}
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_init(&N);
+ mbedtls_mpi_init(&E);
+ mbedtls_mpi_init(&D);
+ mbedtls_mpi_init(&P);
+ mbedtls_mpi_init(&Q);
+#endif
switch(key->type) {
case SSH_KEYTYPE_RSA: {
@@ -376,11 +431,26 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA);
mbedtls_pk_setup(new->rsa, pk_info);
- if (mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) &&
- mbedtls_pk_can_do(new->rsa, MBEDTLS_PK_RSA)) {
- rsa = mbedtls_pk_rsa(*key->rsa);
- new_rsa = mbedtls_pk_rsa(*new->rsa);
+ if (!mbedtls_pk_can_do(key->rsa, MBEDTLS_PK_RSA) ||
+ !mbedtls_pk_can_do(new->rsa, MBEDTLS_PK_RSA))
+ {
+ goto fail;
+ }
+
+ rsa = mbedtls_pk_rsa(*key->rsa);
+ new_rsa = mbedtls_pk_rsa(*new->rsa);
+ if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, &N, &P, &Q, &D, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+ rc = mbedtls_rsa_import(new_rsa, &N, &P, &Q, &D, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+#else
rc = mbedtls_mpi_copy(&new_rsa->N, &rsa->N);
if (rc != 0) {
goto fail;
@@ -390,42 +460,70 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
if (rc != 0) {
goto fail;
}
+
new_rsa->len = (mbedtls_mpi_bitlen(&new_rsa->N) + 7) >> 3;
- if (!demote && (key->flags & SSH_KEY_FLAG_PRIVATE)) {
- rc = mbedtls_mpi_copy(&new_rsa->D, &rsa->D);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->D, &rsa->D);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->P, &rsa->P);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->P, &rsa->P);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->Q, &rsa->Q);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->Q, &rsa->Q);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->DP, &rsa->DP);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->DP, &rsa->DP);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->DQ, &rsa->DQ);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->DQ, &rsa->DQ);
+ if (rc != 0) {
+ goto fail;
+ }
- rc = mbedtls_mpi_copy(&new_rsa->QP, &rsa->QP);
- if (rc != 0) {
- goto fail;
- }
+ rc = mbedtls_mpi_copy(&new_rsa->QP, &rsa->QP);
+ if (rc != 0) {
+ goto fail;
}
+#endif
} else {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+ rc = mbedtls_rsa_import(new_rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+#else
+ rc = mbedtls_mpi_copy(&new_rsa->N, &rsa->N);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ rc = mbedtls_mpi_copy(&new_rsa->E, &rsa->E);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ new_rsa->len = (mbedtls_mpi_bitlen(&new_rsa->N) + 7) >> 3;
+#endif
+ }
+
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_complete(new_rsa);
+ if (rc != 0) {
goto fail;
}
+#endif
break;
}
@@ -443,12 +541,14 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
mbedtls_ecdsa_init(new->ecdsa);
if (demote && ssh_key_is_private(key)) {
- rc = mbedtls_ecp_copy(&new->ecdsa->Q, &key->ecdsa->Q);
+ rc = mbedtls_ecp_copy(&new->ecdsa->MBEDTLS_PRIVATE(Q),
+ &key->ecdsa->MBEDTLS_PRIVATE(Q));
if (rc != 0) {
goto fail;
}
- rc = mbedtls_ecp_group_copy(&new->ecdsa->grp, &key->ecdsa->grp);
+ rc = mbedtls_ecp_group_copy(&new->ecdsa->MBEDTLS_PRIVATE(grp),
+ &key->ecdsa->MBEDTLS_PRIVATE(grp));
if (rc != 0) {
goto fail;
}
@@ -467,10 +567,19 @@ ssh_key pki_key_dup(const ssh_key key, int demote)
goto fail;
}
- return new;
+ goto cleanup;
+
fail:
- ssh_key_free(new);
- return NULL;
+ SSH_KEY_FREE(new);
+cleanup:
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+ mbedtls_mpi_free(&D);
+ mbedtls_mpi_free(&P);
+ mbedtls_mpi_free(&Q);
+#endif
+ return new;
}
int pki_key_generate_rsa(ssh_key key, int parameter)
@@ -508,36 +617,140 @@ int pki_key_generate_rsa(ssh_key key, int parameter)
int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
{
- switch (k1->type) {
+ int rc = 0;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi N1;
+ mbedtls_mpi N2;
+ mbedtls_mpi P1;
+ mbedtls_mpi P2;
+ mbedtls_mpi Q1;
+ mbedtls_mpi Q2;
+ mbedtls_mpi E1;
+ mbedtls_mpi E2;
+
+ mbedtls_mpi_init(&N1);
+ mbedtls_mpi_init(&N2);
+ mbedtls_mpi_init(&P1);
+ mbedtls_mpi_init(&P2);
+ mbedtls_mpi_init(&Q1);
+ mbedtls_mpi_init(&Q2);
+ mbedtls_mpi_init(&E1);
+ mbedtls_mpi_init(&E2);
+#endif
+
+ switch (ssh_key_type_plain(k1->type)) {
case SSH_KEYTYPE_RSA: {
mbedtls_rsa_context *rsa1, *rsa2;
- if (mbedtls_pk_can_do(k1->rsa, MBEDTLS_PK_RSA) &&
- mbedtls_pk_can_do(k2->rsa, MBEDTLS_PK_RSA)) {
- if (mbedtls_pk_get_type(k1->rsa) != mbedtls_pk_get_type(k2->rsa) ||
- mbedtls_pk_get_bitlen(k1->rsa) !=
- mbedtls_pk_get_bitlen(k2->rsa)) {
- return 1;
+ if (!mbedtls_pk_can_do(k1->rsa, MBEDTLS_PK_RSA) ||
+ !mbedtls_pk_can_do(k2->rsa, MBEDTLS_PK_RSA))
+ {
+ break;
+ }
+
+ if (mbedtls_pk_get_type(k1->rsa) != mbedtls_pk_get_type(k2->rsa) ||
+ mbedtls_pk_get_bitlen(k1->rsa) !=
+ mbedtls_pk_get_bitlen(k2->rsa))
+ {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (what == SSH_KEY_CMP_PUBLIC) {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rc = mbedtls_rsa_export(rsa1, &N1, NULL, NULL, NULL, &E1);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
}
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ rc = mbedtls_rsa_export(rsa2, &N2, NULL, NULL, NULL, &E2);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&N1, &N2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&E1, &E2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+#else
rsa1 = mbedtls_pk_rsa(*k1->rsa);
rsa2 = mbedtls_pk_rsa(*k2->rsa);
if (mbedtls_mpi_cmp_mpi(&rsa1->N, &rsa2->N) != 0) {
- return 1;
+ rc = 1;
+ goto cleanup;
}
if (mbedtls_mpi_cmp_mpi(&rsa1->E, &rsa2->E) != 0) {
- return 1;
+ rc = 1;
+ goto cleanup;
+ }
+#endif
+ } else if (what == SSH_KEY_CMP_PRIVATE) {
+#if MBEDTLS_VERSION_MAJOR > 2
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rc = mbedtls_rsa_export(rsa1, &N1, &P1, &Q1, NULL, &E1);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
}
- if (what == SSH_KEY_CMP_PRIVATE) {
- if (mbedtls_mpi_cmp_mpi(&rsa1->P, &rsa2->P) != 0) {
- return 1;
- }
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ rc = mbedtls_rsa_export(rsa2, &N2, &P2, &Q2, NULL, &E2);
+ if (rc != 0) {
+ rc = 1;
+ goto cleanup;
+ }
- if (mbedtls_mpi_cmp_mpi(&rsa1->Q, &rsa2->Q) != 0) {
- return 1;
- }
+ if (mbedtls_mpi_cmp_mpi(&N1, &N2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&E1, &E2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&P1, &P2) != 0) {
+ rc = 1;
+ goto cleanup;
}
+
+ if (mbedtls_mpi_cmp_mpi(&Q1, &Q2) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+#else
+ rsa1 = mbedtls_pk_rsa(*k1->rsa);
+ rsa2 = mbedtls_pk_rsa(*k2->rsa);
+ if (mbedtls_mpi_cmp_mpi(&rsa1->N, &rsa2->N) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->E, &rsa2->E) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->P, &rsa2->P) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ if (mbedtls_mpi_cmp_mpi(&rsa1->Q, &rsa2->Q) != 0) {
+ rc = 1;
+ goto cleanup;
+ }
+#endif
}
break;
}
@@ -548,25 +761,39 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
mbedtls_ecp_keypair *ecdsa1 = k1->ecdsa;
mbedtls_ecp_keypair *ecdsa2 = k2->ecdsa;
- if (ecdsa1->grp.id != ecdsa2->grp.id) {
- return 1;
+ if (ecdsa1->MBEDTLS_PRIVATE(grp).id !=
+ ecdsa2->MBEDTLS_PRIVATE(grp).id) {
+ rc = 1;
+ goto cleanup;
}
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.X, &ecdsa2->Q.X)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X),
+ &ecdsa2->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)))
+ {
+ rc = 1;
+ goto cleanup;
}
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.Y, &ecdsa2->Q.Y)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y),
+ &ecdsa2->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)))
+ {
+ rc = 1;
+ goto cleanup;
}
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->Q.Z, &ecdsa2->Q.Z)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z),
+ &ecdsa2->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z)))
+ {
+ rc = 1;
+ goto cleanup;
}
if (what == SSH_KEY_CMP_PRIVATE) {
- if (mbedtls_mpi_cmp_mpi(&ecdsa1->d, &ecdsa2->d)) {
- return 1;
+ if (mbedtls_mpi_cmp_mpi(&ecdsa1->MBEDTLS_PRIVATE(d),
+ &ecdsa2->MBEDTLS_PRIVATE(d)))
+ {
+ rc = 1;
+ goto cleanup;
}
}
@@ -575,12 +802,25 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
/* ed25519 keys handled globally */
- return 0;
+ rc = 0;
+ break;
default:
- return 1;
- }
-
- return 0;
+ rc = 1;
+ break;
+ }
+
+cleanup:
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N1);
+ mbedtls_mpi_free(&N2);
+ mbedtls_mpi_free(&P1);
+ mbedtls_mpi_free(&P2);
+ mbedtls_mpi_free(&Q1);
+ mbedtls_mpi_free(&Q2);
+ mbedtls_mpi_free(&E1);
+ mbedtls_mpi_free(&E2);
+#endif
+ return rc;
}
ssh_string make_ecpoint_string(const mbedtls_ecp_group *g, const
@@ -638,15 +878,28 @@ static const char* pki_key_ecdsa_nid_to_char(int nid)
return "unknown";
}
-ssh_string pki_publickey_to_blob(const ssh_key key)
+ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
{
ssh_buffer buffer = NULL;
ssh_string type_s = NULL;
ssh_string e = NULL;
ssh_string n = NULL;
ssh_string str = NULL;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi E;
+ mbedtls_mpi N;
+ mbedtls_mpi D;
+ mbedtls_mpi IQMP;
+ mbedtls_mpi P;
+ mbedtls_mpi Q;
+#endif
int rc;
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_init(&E);
+ mbedtls_mpi_init(&N);
+#endif
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
return NULL;
@@ -685,31 +938,151 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
rsa = mbedtls_pk_rsa(*key->rsa);
- e = ssh_make_bignum_string(&rsa->E);
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, &N, NULL, NULL, NULL, &E);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ e = ssh_make_bignum_string(&E);
if (e == NULL) {
goto fail;
}
- n = ssh_make_bignum_string(&rsa->N);
+ n = ssh_make_bignum_string(&N);
if (n == NULL) {
goto fail;
}
-
- if (ssh_buffer_add_ssh_string(buffer, e) < 0) {
+#else
+ e = ssh_make_bignum_string(&rsa->E);
+ if (e == NULL) {
goto fail;
}
- if (ssh_buffer_add_ssh_string(buffer, n) < 0) {
+ n = ssh_make_bignum_string(&rsa->N);
+ if (n == NULL) {
goto fail;
}
+#endif
+
+ if (type == SSH_KEY_PUBLIC) {
+ /* The N and E parts are swapped in the public key export ! */
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+ } else if (type == SSH_KEY_PRIVATE) {
+ ssh_string p = NULL;
+ ssh_string q = NULL;
+ ssh_string d = NULL;
+ ssh_string iqmp = NULL;
+
+ rc = ssh_buffer_add_ssh_string(buffer, n);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, e);
+ if (rc < 0) {
+ goto fail;
+ }
+
+#if MBEDTLS_VERSION_MAJOR > 2
+ rc = mbedtls_rsa_export(rsa, NULL, &P, &Q, &D, NULL);
+ if (rc != 0) {
+ goto fail;
+ }
+
+ p = ssh_make_bignum_string(&P);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ q = ssh_make_bignum_string(&Q);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ d = ssh_make_bignum_string(&D);
+ if (d == NULL) {
+ goto fail;
+ }
+ rc = mbedtls_rsa_export_crt(rsa, NULL, NULL, &IQMP);
+ if (rc != 0) {
+ goto fail;
+ }
+ iqmp = ssh_make_bignum_string(&IQMP);
+ if (iqmp == NULL) {
+ goto fail;
+ }
+
+#else
+ p = ssh_make_bignum_string(&rsa->P);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ q = ssh_make_bignum_string(&rsa->Q);
+ if (q == NULL) {
+ goto fail;
+ }
+
+ d = ssh_make_bignum_string(&rsa->D);
+ if (d == NULL) {
+ goto fail;
+ }
+
+ iqmp = ssh_make_bignum_string(&rsa->QP);
+ if (iqmp == NULL) {
+ goto fail;
+ }
+#endif
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, iqmp);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, p);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, q);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ ssh_string_burn(iqmp);
+ SSH_STRING_FREE(iqmp);
+ iqmp = NULL;
+ ssh_string_burn(p);
+ SSH_STRING_FREE(p);
+ p = NULL;
+ ssh_string_burn(q);
+ SSH_STRING_FREE(q);
+ q = NULL;
+ }
ssh_string_burn(e);
SSH_STRING_FREE(e);
e = NULL;
ssh_string_burn(n);
SSH_STRING_FREE(n);
n = NULL;
-
break;
}
case SSH_KEYTYPE_ECDSA_P256:
@@ -730,7 +1103,8 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
return NULL;
}
- e = make_ecpoint_string(&key->ecdsa->grp, &key->ecdsa->Q);
+ e = make_ecpoint_string(&key->ecdsa->MBEDTLS_PRIVATE(grp),
+ &key->ecdsa->MBEDTLS_PRIVATE(Q));
if (e == NULL) {
SSH_BUFFER_FREE(buffer);
@@ -746,21 +1120,51 @@ ssh_string pki_publickey_to_blob(const ssh_key key)
SSH_STRING_FREE(e);
e = NULL;
- if (key->type == SSH_KEYTYPE_SK_ECDSA &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
- }
+ if (type == SSH_KEY_PRIVATE) {
+ ssh_string d = NULL;
+ d = ssh_make_bignum_string(&key->ecdsa->MBEDTLS_PRIVATE(d));
+
+ if (d == NULL) {
+ SSH_BUFFER_FREE(buffer);
+ return NULL;
+ }
+
+ rc = ssh_buffer_add_ssh_string(buffer, d);
+ if (rc < 0) {
+ goto fail;
+ }
+
+ ssh_string_burn(d);
+ SSH_STRING_FREE(d);
+ d = NULL;
+ } else if (key->type == SSH_KEYTYPE_SK_ECDSA) {
+ /* public key can contain certificate sk information */
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
break;
case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519:
- rc = pki_ed25519_public_key_to_blob(buffer, key);
- if (rc != SSH_OK) {
- goto fail;
- }
- if (key->type == SSH_KEYTYPE_SK_ED25519 &&
- ssh_buffer_add_ssh_string(buffer, key->sk_application) < 0) {
- goto fail;
+ if (type == SSH_KEY_PUBLIC) {
+ rc = pki_ed25519_public_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
+ /* public key can contain certificate sk information */
+ if (key->type == SSH_KEYTYPE_SK_ED25519) {
+ rc = ssh_buffer_add_ssh_string(buffer, key->sk_application);
+ if (rc < 0) {
+ goto fail;
+ }
+ }
+ } else {
+ rc = pki_ed25519_private_key_to_blob(buffer, key);
+ if (rc == SSH_ERROR) {
+ goto fail;
+ }
}
break;
default:
@@ -779,6 +1183,10 @@ makestring:
}
SSH_BUFFER_FREE(buffer);
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+#endif
return str;
fail:
SSH_BUFFER_FREE(buffer);
@@ -788,6 +1196,10 @@ fail:
SSH_STRING_FREE(e);
ssh_string_burn(n);
SSH_STRING_FREE(n);
+#if MBEDTLS_VERSION_MAJOR > 2
+ mbedtls_mpi_free(&N);
+ mbedtls_mpi_free(&E);
+#endif
return NULL;
}
@@ -845,15 +1257,20 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
return NULL;
}
- ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b));
+ rc = ssh_string_fill(sig_blob, ssh_buffer_get(b), ssh_buffer_get_len(b));
SSH_BUFFER_FREE(b);
+ if (rc < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
+
break;
}
case SSH_KEYTYPE_ED25519:
sig_blob = pki_ed25519_signature_to_blob(sig);
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature key type: %s",
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature key type: %s",
sig->type_c);
return NULL;
}
@@ -873,20 +1290,20 @@ static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, const
size_t len = ssh_string_len(sig_blob);
if (pubkey->rsa == NULL) {
- SSH_LOG(SSH_LOG_WARN, "Pubkey RSA field NULL");
+ SSH_LOG(SSH_LOG_TRACE, "Pubkey RSA field NULL");
goto errout;
}
rsalen = mbedtls_pk_get_bitlen(pubkey->rsa) / 8;
if (len > rsalen) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Signature is too big: %lu > %lu",
(unsigned long) len,
(unsigned long) rsalen);
goto errout;
}
#ifdef DEBUG_CRYPTO
- SSH_LOG(SSH_LOG_WARN, "RSA signature len: %lu", (unsigned long)len);
+ SSH_LOG(SSH_LOG_TRACE, "RSA signature len: %lu", (unsigned long)len);
ssh_log_hexdump("RSA signature", ssh_string_data(sig_blob), len);
#endif
@@ -927,7 +1344,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
int rc;
if (ssh_key_type_plain(pubkey->type) != type) {
- SSH_LOG(SSH_LOG_WARN,
+ SSH_LOG(SSH_LOG_TRACE,
"Incompatible public key provided (%d) expecting (%d)",
type,
pubkey->type);
@@ -1012,7 +1429,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
}
if (rlen != 0) {
- SSH_LOG(SSH_LOG_WARN, "Signature has remaining bytes in inner "
+ SSH_LOG(SSH_LOG_TRACE, "Signature has remaining bytes in inner "
"sigblob: %lu",
(unsigned long)rlen);
ssh_signature_free(sig);
@@ -1030,7 +1447,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
}
break;
default:
- SSH_LOG(SSH_LOG_WARN, "Unknown signature type");
+ SSH_LOG(SSH_LOG_TRACE, "Unknown signature type");
return NULL;
}
@@ -1046,6 +1463,7 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
mbedtls_md_type_t md = 0;
unsigned char *sig = NULL;
size_t slen;
+ size_t sig_size;
int ok;
switch (hash_type) {
@@ -1060,11 +1478,12 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
break;
case SSH_DIGEST_AUTO:
default:
- SSH_LOG(SSH_LOG_WARN, "Incompatible key algorithm");
+ SSH_LOG(SSH_LOG_TRACE, "Incompatible key algorithm");
return NULL;
}
- sig = malloc(mbedtls_pk_get_bitlen(privkey) / 8);
+ sig_size = mbedtls_pk_get_bitlen(privkey) / 8;
+ sig = malloc(sig_size);
if (sig == NULL) {
return NULL;
}
@@ -1074,6 +1493,9 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
digest,
dlen,
sig,
+#if MBEDTLS_VERSION_MAJOR > 2
+ sig_size,
+#endif
&slen,
mbedtls_ctr_drbg_random,
ssh_get_mbedtls_ctr_drbg_context());
@@ -1089,9 +1511,13 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
return NULL;
}
- ssh_string_fill(sig_blob, sig, slen);
+ ok = ssh_string_fill(sig_blob, sig, slen);
explicit_bzero(sig, slen);
SAFE_FREE(sig);
+ if (ok < 0) {
+ SSH_STRING_FREE(sig_blob);
+ return NULL;
+ }
return sig_blob;
}
@@ -1136,10 +1562,10 @@ ssh_signature pki_do_sign_hash(const ssh_key privkey,
return NULL;
}
- rc = mbedtls_ecdsa_sign(&privkey->ecdsa->grp,
+ rc = mbedtls_ecdsa_sign(&privkey->ecdsa->MBEDTLS_PRIVATE(grp),
sig->ecdsa_sig.r,
sig->ecdsa_sig.s,
- &privkey->ecdsa->d,
+ &privkey->ecdsa->MBEDTLS_PRIVATE(d),
hash,
hlen,
mbedtls_ctr_drbg_random,
@@ -1342,8 +1768,9 @@ int pki_verify_data_signature(ssh_signature signature,
case SSH_KEYTYPE_ECDSA_P521_CERT01:
case SSH_KEYTYPE_SK_ECDSA:
case SSH_KEYTYPE_SK_ECDSA_CERT01:
- rc = mbedtls_ecdsa_verify(&pubkey->ecdsa->grp, hash, hlen,
- &pubkey->ecdsa->Q, signature->ecdsa_sig.r,
+ rc = mbedtls_ecdsa_verify(&pubkey->ecdsa->MBEDTLS_PRIVATE(grp), hash,
+ hlen, &pubkey->ecdsa->MBEDTLS_PRIVATE(Q),
+ signature->ecdsa_sig.r,
signature->ecdsa_sig.s);
if (rc != 0) {
char error_buf[100];
@@ -1446,18 +1873,19 @@ int pki_privkey_build_ecdsa(ssh_key key, int nid, ssh_string e, ssh_string exp)
goto fail;
}
- rc = mbedtls_ecp_copy(&keypair.Q, &Q);
+ rc = mbedtls_ecp_copy(&keypair.MBEDTLS_PRIVATE(Q), &Q);
if (rc != 0) {
goto fail;
}
- rc = mbedtls_ecp_group_copy(&keypair.grp, &group);
+ rc = mbedtls_ecp_group_copy(&keypair.MBEDTLS_PRIVATE(grp), &group);
if (rc != 0) {
goto fail;
}
- rc = mbedtls_mpi_read_binary(&keypair.d, ssh_string_data(exp),
- ssh_string_len(exp));
+ rc = mbedtls_mpi_read_binary(&keypair.MBEDTLS_PRIVATE(d),
+ ssh_string_data(exp),
+ ssh_string_len(exp));
if (rc != 0) {
goto fail;
}
@@ -1513,17 +1941,17 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
goto fail;
}
- rc = mbedtls_ecp_copy(&keypair.Q, &Q);
+ rc = mbedtls_ecp_copy(&keypair.MBEDTLS_PRIVATE(Q), &Q);
if (rc != 0) {
goto fail;
}
- rc = mbedtls_ecp_group_copy(&keypair.grp, &group);
+ rc = mbedtls_ecp_group_copy(&keypair.MBEDTLS_PRIVATE(grp), &group);
if (rc != 0) {
goto fail;
}
- mbedtls_mpi_init(&keypair.d);
+ mbedtls_mpi_init(&keypair.MBEDTLS_PRIVATE(d));
rc = mbedtls_ecdsa_from_keypair(key->ecdsa, &keypair);
if (rc != 0) {
@@ -1583,36 +2011,39 @@ int pki_key_generate_ecdsa(ssh_key key, int parameter)
return SSH_OK;
}
-int pki_privkey_build_dss(ssh_key key, ssh_string p, ssh_string q, ssh_string g,
- ssh_string pubkey, ssh_string privkey)
-{
- (void) key;
- (void) p;
- (void) q;
- (void) g;
- (void) pubkey;
- (void) privkey;
- return SSH_ERROR;
-}
-
-int pki_pubkey_build_dss(ssh_key key, ssh_string p, ssh_string q, ssh_string g,
- ssh_string pubkey)
-{
- (void) key;
- (void) p;
- (void) q;
- (void) g;
- (void) pubkey;
- return SSH_ERROR;
-}
-
-int pki_key_generate_dss(ssh_key key, int parameter)
+int ssh_key_size(ssh_key key)
{
- (void) key;
- (void) parameter;
- return SSH_ERROR;
+ switch (key->type) {
+ case SSH_KEYTYPE_RSA:
+ case SSH_KEYTYPE_RSA_CERT01:
+ case SSH_KEYTYPE_RSA1:
+ return mbedtls_pk_get_bitlen(key->rsa);
+ case SSH_KEYTYPE_ECDSA_P256:
+ case SSH_KEYTYPE_ECDSA_P256_CERT01:
+ case SSH_KEYTYPE_SK_ECDSA:
+ case SSH_KEYTYPE_SK_ECDSA_CERT01:
+ return 256;
+ case SSH_KEYTYPE_ECDSA_P384:
+ case SSH_KEYTYPE_ECDSA_P384_CERT01:
+ return 384;
+ case SSH_KEYTYPE_ECDSA_P521:
+ case SSH_KEYTYPE_ECDSA_P521_CERT01:
+ return 521;
+ case SSH_KEYTYPE_ED25519:
+ case SSH_KEYTYPE_ED25519_CERT01:
+ case SSH_KEYTYPE_SK_ED25519:
+ case SSH_KEYTYPE_SK_ED25519_CERT01:
+ /* ed25519 keys have fixed size */
+ return 255;
+ case SSH_KEYTYPE_DSS: /* deprecated */
+ case SSH_KEYTYPE_DSS_CERT01: /* deprecated */
+ case SSH_KEYTYPE_UNKNOWN:
+ default:
+ return SSH_ERROR;
+ }
}
+#ifdef WITH_PKCS11_URI
int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type)
{
(void) uri_name;
@@ -1622,4 +2053,5 @@ int pki_uri_import(const char *uri_name, ssh_key *key, enum ssh_key_e key_type)
"mbedcrypto does not support PKCS #11");
return SSH_ERROR;
}
+#endif /* WITH_PKCS11_URI */
#endif /* HAVE_LIBMBEDCRYPTO */
diff --git a/src/poll.c b/src/poll.c
index 46206949..8f81c11c 100644
--- a/src/poll.c
+++ b/src/poll.c
@@ -44,14 +44,14 @@
#endif
/**
- * @defgroup libssh_poll The SSH poll functions.
+ * @defgroup libssh_poll The SSH poll functions
* @ingroup libssh
*
* Add a generic way to handle sockets asynchronously.
*
* It's based on poll objects, each of which store a socket, its events and a
* callback, which gets called whenever an event is set. The poll objects are
- * attached to a poll context, which should be allocated on per thread basis.
+ * attached to a poll context, which should be allocated on a per thread basis.
*
* Polling the poll context will poll all the attached poll objects and call
* their callbacks (handlers) if any of the socket events are set. This should
@@ -68,7 +68,7 @@ struct ssh_poll_handle_struct {
size_t idx;
} x;
short events;
- int lock;
+ uint32_t lock_cnt;
ssh_poll_callback cb;
void *cb_data;
};
@@ -84,15 +84,18 @@ struct ssh_poll_ctx_struct {
#ifdef HAVE_POLL
#include <poll.h>
-void ssh_poll_init(void) {
+void ssh_poll_init(void)
+{
return;
}
-void ssh_poll_cleanup(void) {
+void ssh_poll_cleanup(void)
+{
return;
}
-int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
+int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
+{
return poll((struct pollfd *) fds, nfds, timeout);
}
@@ -210,8 +213,8 @@ static short bsd_socket_compute_revents(int fd, short events)
* poll implementation.
*
* Keep in mind that select is terribly inefficient. The interface is simply not
- * meant to be used with maximum descriptor value greater, say, 32 or so. With
- * a value as high as 1024 on Linux you'll pay dearly in every single call.
+ * meant to be used with maximum descriptor value greater than, say, 32 or so.
+ * With a value as high as 1024 on Linux you'll pay dearly in every single call.
* poll() will be orders of magnitude faster.
*/
static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
@@ -246,19 +249,17 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
}
#endif
- if (fds[i].events & (POLLIN | POLLRDNORM)) {
- FD_SET (fds[i].fd, &readfds);
- }
+ // we use the readfds to get POLLHUP and POLLERR, which are provided even when not requested
+ FD_SET (fds[i].fd, &readfds);
+
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
FD_SET (fds[i].fd, &writefds);
}
if (fds[i].events & (POLLPRI | POLLRDBAND)) {
FD_SET (fds[i].fd, &exceptfds);
}
- if (fds[i].fd > max_fd &&
- (fds[i].events & (POLLIN | POLLOUT | POLLPRI |
- POLLRDNORM | POLLRDBAND |
- POLLWRNORM | POLLWRBAND))) {
+
+ if (fds[i].fd > max_fd) {
max_fd = fds[i].fd;
rc = 0;
}
@@ -286,7 +287,7 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
if (rc < 0) {
return -1;
}
- /* A timeout occured */
+ /* A timeout occurred */
if (rc == 0) {
return 0;
}
@@ -335,21 +336,24 @@ int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
/**
* @brief Allocate a new poll object, which could be used within a poll context.
*
- * @param fd Socket that will be polled.
- * @param events Poll events that will be monitored for the socket. i.e.
- * POLLIN, POLLPRI, POLLOUT
- * @param cb Function to be called if any of the events are set.
- * The prototype of cb is:
- * int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd,
- * int revents, void *userdata);
- * @param userdata Userdata to be passed to the callback function. NULL if
- * not needed.
+ * @param[in] fd Socket that will be polled.
+ * @param[in] events Poll events that will be monitored for the socket.
+ * i.e. POLLIN, POLLPRI, POLLOUT
+ * @param[in] cb Function to be called if any of the events are set.
+ * The prototype of cb is:
+ * int (*ssh_poll_callback)(ssh_poll_handle p,
+ * socket_t fd,
+ * int revents,
+ * void *userdata);
+ * @param[in] userdata Userdata to be passed to the callback function.
+ * NULL if not needed.
*
- * @return A new poll object, NULL on error
+ * @return A new poll object, NULL on error
*/
-ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb,
- void *userdata) {
+ssh_poll_handle
+ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb, void *userdata)
+{
ssh_poll_handle p;
p = malloc(sizeof(struct ssh_poll_handle_struct));
@@ -373,12 +377,13 @@ ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb,
* @param p Pointer to an already allocated poll object.
*/
-void ssh_poll_free(ssh_poll_handle p) {
- if(p->ctx != NULL){
- ssh_poll_ctx_remove(p->ctx,p);
- p->ctx=NULL;
- }
- SAFE_FREE(p);
+void ssh_poll_free(ssh_poll_handle p)
+{
+ if (p->ctx != NULL) {
+ ssh_poll_ctx_remove(p->ctx, p);
+ p->ctx = NULL;
+ }
+ SAFE_FREE(p);
}
/**
@@ -388,8 +393,9 @@ void ssh_poll_free(ssh_poll_handle p) {
*
* @return Poll context or NULL if the poll object isn't attached.
*/
-ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) {
- return p->ctx;
+ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p)
+{
+ return p->ctx;
}
/**
@@ -399,22 +405,31 @@ ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) {
*
* @return Poll events.
*/
-short ssh_poll_get_events(ssh_poll_handle p) {
- return p->events;
+short ssh_poll_get_events(ssh_poll_handle p)
+{
+ return p->events;
}
/**
* @brief Set the events of a poll object. The events will also be propagated
- * to an associated poll context.
+ * to an associated poll context unless the fd is locked. In that case,
+ * only the POLLOUT can be set.
*
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
-void ssh_poll_set_events(ssh_poll_handle p, short events) {
- p->events = events;
- if (p->ctx != NULL && !p->lock) {
- p->ctx->pollfds[p->x.idx].events = events;
- }
+void ssh_poll_set_events(ssh_poll_handle p, short events)
+{
+ p->events = events;
+ if (p->ctx != NULL) {
+ if (p->lock_cnt == 0) {
+ p->ctx->pollfds[p->x.idx].events = events;
+ } else if (!(p->ctx->pollfds[p->x.idx].events & POLLOUT)) {
+ /* if locked, allow only setting POLLOUT to prevent recursive
+ * callbacks */
+ p->ctx->pollfds[p->x.idx].events = events & POLLOUT;
+ }
+ }
}
/**
@@ -424,12 +439,13 @@ void ssh_poll_set_events(ssh_poll_handle p, short events) {
* @param p Pointer to an already allocated poll object.
* @param fd New file descriptor.
*/
-void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) {
- if (p->ctx != NULL) {
- p->ctx->pollfds[p->x.idx].fd = fd;
- } else {
- p->x.fd = fd;
- }
+void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd)
+{
+ if (p->ctx != NULL) {
+ p->ctx->pollfds[p->x.idx].fd = fd;
+ } else {
+ p->x.fd = fd;
+ }
}
/**
@@ -439,8 +455,9 @@ void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) {
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
-void ssh_poll_add_events(ssh_poll_handle p, short events) {
- ssh_poll_set_events(p, ssh_poll_get_events(p) | events);
+void ssh_poll_add_events(ssh_poll_handle p, short events)
+{
+ ssh_poll_set_events(p, ssh_poll_get_events(p) | events);
}
/**
@@ -450,8 +467,9 @@ void ssh_poll_add_events(ssh_poll_handle p, short events) {
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
-void ssh_poll_remove_events(ssh_poll_handle p, short events) {
- ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events);
+void ssh_poll_remove_events(ssh_poll_handle p, short events)
+{
+ ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events);
}
/**
@@ -462,12 +480,13 @@ void ssh_poll_remove_events(ssh_poll_handle p, short events) {
* @return Raw socket.
*/
-socket_t ssh_poll_get_fd(ssh_poll_handle p) {
- if (p->ctx != NULL) {
- return p->ctx->pollfds[p->x.idx].fd;
- }
+socket_t ssh_poll_get_fd(ssh_poll_handle p)
+{
+ if (p->ctx != NULL) {
+ return p->ctx->pollfds[p->x.idx].fd;
+ }
- return p->x.fd;
+ return p->x.fd;
}
/**
* @brief Set the callback of a poll object.
@@ -477,11 +496,12 @@ socket_t ssh_poll_get_fd(ssh_poll_handle p) {
* @param userdata Userdata to be passed to the callback function. NULL if
* not needed.
*/
-void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata) {
- if (cb != NULL) {
- p->cb = cb;
- p->cb_data = userdata;
- }
+void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata)
+{
+ if (cb != NULL) {
+ p->cb = cb;
+ p->cb_data = userdata;
+ }
}
/**
@@ -495,7 +515,8 @@ void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userda
* for the next 5. Set it to 0 if you want to use the
* library's default value.
*/
-ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) {
+ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size)
+{
ssh_poll_ctx ctx;
ctx = malloc(sizeof(struct ssh_poll_ctx_struct));
@@ -518,25 +539,27 @@ ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) {
*
* @param ctx Pointer to an already allocated poll context.
*/
-void ssh_poll_ctx_free(ssh_poll_ctx ctx) {
- if (ctx->polls_allocated > 0) {
- while (ctx->polls_used > 0){
- ssh_poll_handle p = ctx->pollptrs[0];
- /*
- * The free function calls ssh_poll_ctx_remove() and decrements
- * ctx->polls_used
- */
- ssh_poll_free(p);
- }
+void ssh_poll_ctx_free(ssh_poll_ctx ctx)
+{
+ if (ctx->polls_allocated > 0) {
+ while (ctx->polls_used > 0){
+ ssh_poll_handle p = ctx->pollptrs[0];
+ /*
+ * The free function calls ssh_poll_ctx_remove() and decrements
+ * ctx->polls_used
+ */
+ ssh_poll_free(p);
+ }
- SAFE_FREE(ctx->pollptrs);
- SAFE_FREE(ctx->pollfds);
- }
+ SAFE_FREE(ctx->pollptrs);
+ SAFE_FREE(ctx->pollfds);
+ }
- SAFE_FREE(ctx);
+ SAFE_FREE(ctx);
}
-static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) {
+static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size)
+{
ssh_poll_handle *pollptrs;
ssh_pollfd_t *pollfds;
@@ -570,7 +593,8 @@ static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) {
*
* @return 0 on success, < 0 on error
*/
-int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) {
+int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p)
+{
socket_t fd;
if (p->ctx != NULL) {
@@ -604,7 +628,7 @@ int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) {
*/
int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s)
{
- ssh_poll_handle p;
+ ssh_poll_handle p = NULL;
int ret;
p = ssh_socket_get_poll_handle(s);
@@ -622,7 +646,8 @@ int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s)
* @param ctx Pointer to an already allocated poll context.
* @param p Pointer to an already allocated poll object.
*/
-void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
+void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p)
+{
size_t i;
i = p->x.idx;
@@ -648,7 +673,7 @@ void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
* @brief Poll all the sockets associated through a poll object with a
* poll context. If any of the events are set after the poll, the
* call back function of the socket will be called.
- * This function should be called once within the programs main loop.
+ * This function should be called once within the program's main loop.
*
* @param ctx Pointer to an already allocated poll context.
* @param timeout An upper limit on the time for which ssh_poll_ctx() will
@@ -657,7 +682,7 @@ void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
* the poll() function.
* @returns SSH_OK No error.
* SSH_ERROR Error happened during the poll.
- * SSH_AGAIN Timeout occured
+ * SSH_AGAIN Timeout occurred
*/
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
@@ -673,6 +698,15 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
return SSH_ERROR;
}
+ /* Allow only POLLOUT events on locked sockets as that means we are called
+ * recursively and we only want process the POLLOUT events here to flush
+ * output buffer */
+ for (i = 0; i < ctx->polls_used; i++) {
+ /* The lock allows only POLLOUT events: drop the rest */
+ if (ctx->pollptrs[i]->lock_cnt > 0) {
+ ctx->pollfds[i].events &= POLLOUT;
+ }
+ }
ssh_timestamp_init(&ts);
do {
int tm = ssh_timeout_update(&ts, timeout);
@@ -688,17 +722,24 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
used = ctx->polls_used;
for (i = 0; i < used && rc > 0; ) {
- if (!ctx->pollfds[i].revents || ctx->pollptrs[i]->lock) {
+ revents = ctx->pollfds[i].revents;
+ /* Do not pass any other events except for POLLOUT to callback when
+ * called recursively more than 2 times. On s390x the poll will be
+ * spammed with POLLHUP events causing infinite recursion when the user
+ * callback issues some write/flush/poll calls. */
+ if (ctx->pollptrs[i]->lock_cnt > 2) {
+ revents &= POLLOUT;
+ }
+ if (revents == 0) {
i++;
} else {
int ret;
p = ctx->pollptrs[i];
fd = ctx->pollfds[i].fd;
- revents = ctx->pollfds[i].revents;
/* avoid having any event caught during callback */
ctx->pollfds[i].events = 0;
- p->lock = 1;
+ p->lock_cnt++;
if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) {
if (ret == -2) {
return -1;
@@ -709,7 +750,7 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
} else {
ctx->pollfds[i].revents = 0;
ctx->pollfds[i].events = p->events;
- p->lock = 0;
+ p->lock_cnt--;
i++;
}
@@ -727,12 +768,13 @@ int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout)
* @param session SSH session
* @returns the default ssh_poll_ctx
*/
-ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session){
- if(session->default_poll_ctx != NULL)
- return session->default_poll_ctx;
- /* 2 is enough for the default one */
- session->default_poll_ctx = ssh_poll_ctx_new(2);
- return session->default_poll_ctx;
+ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session)
+{
+ if(session->default_poll_ctx != NULL)
+ return session->default_poll_ctx;
+ /* 2 is enough for the default one */
+ session->default_poll_ctx = ssh_poll_ctx_new(2);
+ return session->default_poll_ctx;
}
/* public event API */
@@ -754,10 +796,11 @@ struct ssh_event_struct {
* ssh_session objects and socket fd which are going to be polled at the
* same time as the event context. You would need a single event context
* per thread.
- *
+ *
* @return The ssh_event object on success, NULL on failure.
*/
-ssh_event ssh_event_new(void) {
+ssh_event ssh_event_new(void)
+{
ssh_event event;
event = malloc(sizeof(struct ssh_event_struct));
@@ -784,12 +827,14 @@ ssh_event ssh_event_new(void) {
return event;
}
-static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents,
- void *userdata) {
+static int
+ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents,
+ void *userdata)
+{
struct ssh_event_fd_wrapper *pw = (struct ssh_event_fd_wrapper *)userdata;
(void)p;
- if(pw->cb != NULL) {
+ if (pw->cb != NULL) {
return pw->cb(fd, revents, pw->userdata);
}
return 0;
@@ -812,11 +857,13 @@ static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int rev
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_add_fd(ssh_event event, socket_t fd, short events,
- ssh_event_callback cb, void *userdata) {
+int
+ssh_event_add_fd(ssh_event event, socket_t fd, short events,
+ ssh_event_callback cb, void *userdata)
+{
ssh_poll_handle p;
struct ssh_event_fd_wrapper *pw;
-
+
if(event == NULL || event->ctx == NULL || cb == NULL
|| fd == SSH_INVALID_SOCKET) {
return SSH_ERROR;
@@ -872,7 +919,7 @@ void ssh_event_remove_poll(ssh_event event, ssh_poll_handle p)
}
/**
- * @brief remove the poll handle from session and assign them to a event,
+ * @brief remove the poll handle from session and assign them to an event,
* when used in blocking mode.
*
* @param event The ssh_event object
@@ -881,7 +928,8 @@ void ssh_event_remove_poll(ssh_event event, ssh_poll_handle p)
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_add_session(ssh_event event, ssh_session session) {
+int ssh_event_add_session(ssh_event event, ssh_session session)
+{
ssh_poll_handle p;
#ifdef WITH_SERVER
struct ssh_iterator *iterator;
@@ -933,16 +981,19 @@ int ssh_event_add_session(ssh_event event, ssh_session session) {
*
* @return SSH_ERROR in case of error
*/
-int ssh_event_add_connector(ssh_event event, ssh_connector connector){
+int ssh_event_add_connector(ssh_event event, ssh_connector connector)
+{
return ssh_connector_set_event(connector, event);
}
/**
- * @brief Poll all the sockets and sessions associated through an event object.i
+ * @brief Poll all the sockets and sessions associated through an event object.
*
* If any of the events are set after the poll, the call back functions of the
* sessions or sockets will be called.
* This function should be called once within the programs main loop.
+ * In case of failure, the errno should be consulted to find more information
+ * about the failure set by underlying poll imlpementation.
*
* @param event The ssh_event object to poll.
*
@@ -951,13 +1002,15 @@ int ssh_event_add_connector(ssh_event event, ssh_connector connector){
* means an infinite timeout. This parameter is passed to
* the poll() function.
* @returns SSH_OK on success.
- * SSH_ERROR Error happened during the poll.
- * SSH_AGAIN Timeout occured
+ * SSH_ERROR Error happened during the poll. Check errno to get more
+ * details about why it failed.
+ * SSH_AGAIN Timeout occurred
*/
-int ssh_event_dopoll(ssh_event event, int timeout) {
+int ssh_event_dopoll(ssh_event event, int timeout)
+{
int rc;
- if(event == NULL || event->ctx == NULL) {
+ if (event == NULL || event->ctx == NULL) {
return SSH_ERROR;
}
rc = ssh_poll_ctx_dopoll(event->ctx, timeout);
@@ -973,7 +1026,8 @@ int ssh_event_dopoll(ssh_event event, int timeout) {
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_remove_fd(ssh_event event, socket_t fd) {
+int ssh_event_remove_fd(ssh_event event, socket_t fd)
+{
register size_t i, used;
int rc = SSH_ERROR;
@@ -1019,7 +1073,8 @@ int ssh_event_remove_fd(ssh_event event, socket_t fd) {
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
-int ssh_event_remove_session(ssh_event event, ssh_session session) {
+int ssh_event_remove_session(ssh_event event, ssh_session session)
+{
ssh_poll_handle p;
register size_t i, used;
int rc = SSH_ERROR;
@@ -1027,14 +1082,14 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
struct ssh_iterator *iterator;
#endif
- if(event == NULL || event->ctx == NULL || session == NULL) {
+ if (event == NULL || event->ctx == NULL || session == NULL) {
return SSH_ERROR;
}
used = event->ctx->polls_used;
- for(i = 0; i < used; i++) {
- p = event->ctx->pollptrs[i];
- if(p->session == session){
+ for (i = 0; i < used; i++) {
+ p = event->ctx->pollptrs[i];
+ if (p->session == session) {
/*
* ssh_poll_ctx_remove() decrements
* event->ctx->polls_used
@@ -1054,8 +1109,8 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
}
#ifdef WITH_SERVER
iterator = ssh_list_get_iterator(event->sessions);
- while(iterator != NULL) {
- if((ssh_session)iterator->data == session) {
+ while (iterator != NULL) {
+ if ((ssh_session)iterator->data == session) {
ssh_list_remove(event->sessions, iterator);
/* there should be only one instance of this session */
break;
@@ -1073,7 +1128,8 @@ int ssh_event_remove_session(ssh_event event, ssh_session session) {
* @return SSH_OK on success
* @return SSH_ERROR on failure
*/
-int ssh_event_remove_connector(ssh_event event, ssh_connector connector){
+int ssh_event_remove_connector(ssh_event event, ssh_connector connector)
+{
(void)event;
return ssh_connector_remove_event(connector);
}
@@ -1091,13 +1147,13 @@ void ssh_event_free(ssh_event event)
size_t used, i;
ssh_poll_handle p;
- if(event == NULL) {
+ if (event == NULL) {
return;
}
if (event->ctx != NULL) {
used = event->ctx->polls_used;
- for(i = 0; i < used; i++) {
+ for (i = 0; i < used; i++) {
p = event->ctx->pollptrs[i];
if (p->session != NULL) {
ssh_poll_ctx_remove(event->ctx, p);
@@ -1110,7 +1166,7 @@ void ssh_event_free(ssh_event event)
ssh_poll_ctx_free(event->ctx);
}
#ifdef WITH_SERVER
- if(event->sessions != NULL) {
+ if (event->sessions != NULL) {
ssh_list_free(event->sessions);
}
#endif
diff --git a/src/scp.c b/src/scp.c
index 85d670a4..40f45dbc 100644
--- a/src/scp.c
+++ b/src/scp.c
@@ -37,10 +37,14 @@
*
* SCP protocol over SSH functions
*
+ * @deprecated Please use SFTP instead
+ *
* @{
*/
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Create a new scp session.
*
* @param[in] session The SSH session to use.
@@ -62,7 +66,7 @@ ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location)
{
ssh_scp scp = NULL;
- if (session == NULL) {
+ if (session == NULL || location == NULL) {
goto error;
}
@@ -108,6 +112,8 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Initialize the scp channel.
*
* @param[in] scp The scp context to initialize.
@@ -119,7 +125,7 @@ error:
int ssh_scp_init(ssh_scp scp)
{
int rc;
- char execbuffer[1024] = {0};
+ char execbuffer[PATH_MAX] = {0};
char *quoted_location = NULL;
size_t quoted_location_len = 0;
size_t scp_location_len;
@@ -140,7 +146,7 @@ int ssh_scp_init(ssh_scp scp)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Initializing scp session %s %son location '%s'",
+ SSH_LOG(SSH_LOG_DEBUG, "Initializing scp session %s %son location '%s'",
scp->mode == SSH_SCP_WRITE?"write":"read",
scp->recursive ? "recursive " : "",
scp->location);
@@ -230,6 +236,8 @@ int ssh_scp_init(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Close the scp channel.
*
* @param[in] scp The scp context to close.
@@ -277,6 +285,8 @@ int ssh_scp_close(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Free a scp context.
*
* @param[in] scp The context to free.
@@ -304,6 +314,8 @@ void ssh_scp_free(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Create a directory in a scp in sink mode.
*
* @param[in] scp The scp handle.
@@ -313,13 +325,13 @@ void ssh_scp_free(ssh_scp scp)
* @param[in] mode The UNIX permissions for the new directory, e.g. 0755.
*
* @returns SSH_OK if the directory has been created, SSH_ERROR if
- * an error occured.
+ * an error occurred.
*
* @see ssh_scp_leave_directory()
*/
int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
{
- char buffer[1024] = {0};
+ char buffer[PATH_MAX] = {0};
int rc;
char *dir = NULL;
char *perms = NULL;
@@ -364,7 +376,7 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"SCP pushing directory %s with permissions '%s'",
vis_encoded, perms);
@@ -399,10 +411,12 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Leave a directory.
*
* @returns SSH_OK if the directory has been left, SSH_ERROR if an
- * error occured.
+ * error occurred.
*
* @see ssh_scp_push_directory()
*/
@@ -436,6 +450,8 @@ int ssh_scp_leave_directory(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit
* size.
*
@@ -449,14 +465,14 @@ int ssh_scp_leave_directory(ssh_scp scp)
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
*
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
- * error occured.
+ * error occurred.
*
* @see ssh_scp_push_file()
*/
int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
int mode)
{
- char buffer[1024] = {0};
+ char buffer[PATH_MAX] = {0};
int rc;
char *file = NULL;
char *perms = NULL;
@@ -501,7 +517,7 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
goto error;
}
- SSH_LOG(SSH_LOG_PROTOCOL,
+ SSH_LOG(SSH_LOG_DEBUG,
"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",
vis_encoded, size, perms);
@@ -540,6 +556,8 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Initialize the sending of a file to a scp in sink mode.
*
* @param[in] scp The scp handle.
@@ -552,7 +570,7 @@ error:
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
*
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
- * error occured.
+ * error occurred.
*/
int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
{
@@ -562,6 +580,8 @@ int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
/**
* @internal
*
+ * @deprecated Please use SFTP instead
+ *
* @brief Wait for a response of the scp server.
*
* @param[in] scp The scp handle.
@@ -569,7 +589,7 @@ int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
* @param[out] response A pointer where the response message must be copied if
* any. This pointer must then be free'd.
*
- * @returns The return code, SSH_ERROR a error occured.
+ * @returns The return code, SSH_ERROR a error occurred.
*/
int ssh_scp_response(ssh_scp scp, char **response)
{
@@ -628,6 +648,8 @@ int ssh_scp_response(ssh_scp scp, char **response)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Write into a remote scp file.
*
* @param[in] scp The scp handle.
@@ -637,7 +659,7 @@ int ssh_scp_response(ssh_scp scp, char **response)
* @param[in] len The number of bytes to write.
*
* @returns SSH_OK if the write was successful, SSH_ERROR an error
- * occured while writing.
+ * occurred while writing.
*/
int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
{
@@ -671,7 +693,6 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
scp->processed += w;
} else {
scp->state = SSH_SCP_ERROR;
- //return = channel_get_exit_status(scp->channel);
return SSH_ERROR;
}
@@ -702,6 +723,8 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Read a string on a channel, terminated by '\n'
*
* @param[in] scp The scp handle.
@@ -713,7 +736,7 @@ int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
* null-terminated.
*
* @returns SSH_OK if the string was read, SSH_ERROR if an error
- * occured while reading.
+ * occurred while reading.
*/
int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
{
@@ -748,6 +771,8 @@ int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Wait for a scp request (file, directory).
*
* @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending
@@ -769,7 +794,7 @@ int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
*/
int ssh_scp_pull_request(ssh_scp scp)
{
- char buffer[MAX_BUF_SIZE] = {0};
+ char buffer[PATH_MAX] = {0};
char *mode = NULL;
char *p, *tmp;
uint64_t size;
@@ -800,7 +825,7 @@ int ssh_scp_pull_request(ssh_scp scp)
*p = '\0';
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Received SCP request: '%s'", buffer);
+ SSH_LOG(SSH_LOG_DEBUG, "Received SCP request: '%s'", buffer);
switch(buffer[0]) {
case 'C':
/* File */
@@ -859,7 +884,7 @@ int ssh_scp_pull_request(ssh_scp scp)
return SSH_ERROR;
}
- /* a parsing error occured */
+ /* a parsing error occurred */
error:
SAFE_FREE(name);
SAFE_FREE(mode);
@@ -869,6 +894,8 @@ error:
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Deny the transfer of a file or creation of a directory coming from the
* remote party.
*
@@ -881,7 +908,8 @@ error:
*/
int ssh_scp_deny_request(ssh_scp scp, const char *reason)
{
- char buffer[MAX_BUF_SIZE] = {0};
+ char *buffer = NULL;
+ size_t len;
int rc;
if (scp == NULL) {
@@ -894,8 +922,15 @@ int ssh_scp_deny_request(ssh_scp scp, const char *reason)
return SSH_ERROR;
}
- snprintf(buffer, sizeof(buffer), "%c%s\n", 2, reason);
- rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
+ len = strlen(reason) + 3;
+ buffer = malloc(len);
+ if (buffer == NULL) {
+ return SSH_ERROR;
+ }
+
+ snprintf(buffer, len, "%c%s\n", 2, reason);
+ rc = ssh_channel_write(scp->channel, buffer, len - 1);
+ free(buffer);
if (rc == SSH_ERROR) {
return SSH_ERROR;
}
@@ -907,6 +942,8 @@ int ssh_scp_deny_request(ssh_scp scp, const char *reason)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Accepts transfer of a file or creation of a directory coming from the
* remote party.
*
@@ -943,14 +980,18 @@ int ssh_scp_accept_request(ssh_scp scp)
return SSH_OK;
}
-/** @brief Read from a remote scp file
+/**
+ * @deprecated Please use SFTP instead
+ *
+ * @brief Read from a remote scp file
+ *
* @param[in] scp The scp handle.
*
* @param[in] buffer The destination buffer.
*
* @param[in] size The size of the buffer.
*
- * @returns The nNumber of bytes read, SSH_ERROR if an error occured
+ * @returns The number of bytes read, SSH_ERROR if an error occurred
* while reading.
*/
int ssh_scp_read(ssh_scp scp, void *buffer, size_t size)
@@ -1014,6 +1055,8 @@ int ssh_scp_read(ssh_scp scp, void *buffer, size_t size)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Get the name of the directory or file being pushed from the other
* party.
*
@@ -1030,6 +1073,8 @@ const char *ssh_scp_request_get_filename(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Get the permissions of the directory or file being pushed from the
* other party.
*
@@ -1044,7 +1089,10 @@ int ssh_scp_request_get_permissions(ssh_scp scp)
return scp->request_mode;
}
-/** @brief Get the size of the file being pushed from the other party.
+/**
+ * @deprecated Please use SFTP instead
+ *
+ * @brief Get the size of the file being pushed from the other party.
*
* @returns The numeric size of the file being read.
* @warning The real size may not fit in a 32 bits field and may
@@ -1059,7 +1107,10 @@ size_t ssh_scp_request_get_size(ssh_scp scp)
return (size_t)scp->filelen;
}
-/** @brief Get the size of the file being pushed from the other party.
+/**
+ * @deprecated Please use SFTP instead
+ *
+ * @brief Get the size of the file being pushed from the other party.
*
* @returns The numeric size of the file being read.
*/
@@ -1072,6 +1123,8 @@ uint64_t ssh_scp_request_get_size64(ssh_scp scp)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Convert a scp text mode to an integer.
*
* @param[in] mode The mode to convert, e.g. "0644".
@@ -1085,6 +1138,8 @@ int ssh_scp_integer_mode(const char *mode)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Convert a unix mode into a scp string.
*
* @param[in] mode The mode to convert, e.g. 420 or 0644.
@@ -1100,6 +1155,8 @@ char *ssh_scp_string_mode(int mode)
}
/**
+ * @deprecated Please use SFTP instead
+ *
* @brief Get the warning string from a scp handle.
*
* @param[in] scp The scp handle.
diff --git a/src/server.c b/src/server.c
index 841a1c42..28c3c015 100644
--- a/src/server.c
+++ b/src/server.c
@@ -92,7 +92,11 @@ int server_set_kex(ssh_session session)
size_t len;
int ok;
- ZERO_STRUCTP(server);
+ /* Skip if already set, for example for the rekey or when we do the guessing
+ * it could have been already used to make some protocol decisions. */
+ if (server->methods[0] != NULL) {
+ return SSH_OK;
+ }
ok = ssh_get_random(server->cookie, 16, 0);
if (!ok) {
@@ -113,15 +117,6 @@ int server_set_kex(ssh_session session)
",%s", session->srv.ecdsa_key->type_c);
}
#endif
-#ifdef HAVE_DSA
- if (session->srv.dsa_key != NULL) {
- len = strlen(hostkeys);
- keytype = ssh_key_type(session->srv.dsa_key);
-
- snprintf(hostkeys + len, sizeof(hostkeys) - len,
- ",%s", ssh_key_type_to_char(keytype));
- }
-#endif
if (session->srv.rsa_key != NULL) {
/* We support also the SHA2 variants */
len = strlen(hostkeys);
@@ -160,7 +155,8 @@ int server_set_kex(ssh_session session)
rc = ssh_options_set_algo(session,
SSH_HOSTKEYS,
- kept);
+ kept,
+ &session->opts.wanted_methods[SSH_HOSTKEYS]);
SAFE_FREE(kept);
if (rc < 0) {
return -1;
@@ -191,7 +187,13 @@ int server_set_kex(ssh_session session)
}
}
- return 0;
+ /* Do not append the extensions during rekey */
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
+ return SSH_OK;
+ }
+
+ rc = ssh_kex_append_extensions(session, server);
+ return rc;
}
int ssh_server_init_kex(ssh_session session) {
@@ -281,9 +283,6 @@ ssh_get_key_params(ssh_session session,
int rc;
switch(session->srv.hostkey) {
- case SSH_KEYTYPE_DSS:
- *privkey = session->srv.dsa_key;
- break;
case SSH_KEYTYPE_RSA:
*privkey = session->srv.rsa_key;
break;
@@ -335,117 +334,127 @@ ssh_get_key_params(ssh_session session,
* @brief A function to be called each time a step has been done in the
* connection.
*/
-static void ssh_server_connection_callback(ssh_session session){
+static void ssh_server_connection_callback(ssh_session session)
+{
int rc;
- switch(session->session_state){
- case SSH_SESSION_STATE_NONE:
- case SSH_SESSION_STATE_CONNECTING:
- case SSH_SESSION_STATE_SOCKET_CONNECTED:
- break;
- case SSH_SESSION_STATE_BANNER_RECEIVED:
- if (session->clientbanner == NULL) {
+ switch (session->session_state) {
+ case SSH_SESSION_STATE_NONE:
+ case SSH_SESSION_STATE_CONNECTING:
+ case SSH_SESSION_STATE_SOCKET_CONNECTED:
+ break;
+ case SSH_SESSION_STATE_BANNER_RECEIVED:
+ if (session->clientbanner == NULL) {
+ goto error;
+ }
+ set_status(session, 0.4f);
+ SSH_LOG(SSH_LOG_DEBUG,
+ "SSH client banner: %s", session->clientbanner);
+
+ /* Here we analyze the different protocols the server allows. */
+ rc = ssh_analyze_banner(session, 1);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL,
+ "No version of SSH protocol usable (banner: %s)",
+ session->clientbanner);
+ goto error;
+ }
+
+ /* from now, the packet layer is handling incoming packets */
+ ssh_packet_register_socket_callback(session, session->socket);
+
+ ssh_packet_set_default_callbacks(session);
+ set_status(session, 0.5f);
+ session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
+ rc = ssh_send_kex(session);
+ if (rc < 0) {
+ goto error;
+ }
+ break;
+ case SSH_SESSION_STATE_INITIAL_KEX:
+ /* TODO: This state should disappear in favor of get_key handle */
+ break;
+ case SSH_SESSION_STATE_KEXINIT_RECEIVED:
+ set_status(session, 0.6f);
+ if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
+ rc = server_set_kex(session);
+ if (rc == SSH_ERROR) {
goto error;
}
- set_status(session, 0.4f);
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SSH client banner: %s", session->clientbanner);
-
- /* Here we analyze the different protocols the server allows. */
- rc = ssh_analyze_banner(session, 1);
+ /* We are in a rekeying, so we need to send the server kex */
+ rc = ssh_send_kex(session);
if (rc < 0) {
- ssh_set_error(session, SSH_FATAL,
- "No version of SSH protocol usable (banner: %s)",
- session->clientbanner);
goto error;
}
+ }
+ ssh_list_kex(&session->next_crypto->client_kex); // log client kex
+ rc = ssh_kex_select_methods(session);
+ if (rc < 0) {
+ goto error;
+ }
+ rc = crypt_set_algorithms_server(session);
+ if (rc == SSH_ERROR) {
+ goto error;
+ }
+ set_status(session, 0.8f);
+ session->session_state = SSH_SESSION_STATE_DH;
+ break;
+ case SSH_SESSION_STATE_DH:
+ if (session->dh_handshake_state == DH_STATE_FINISHED) {
- /* from now, the packet layer is handling incoming packets */
- session->socket_callbacks.data=ssh_packet_socket_callback;
- ssh_packet_register_socket_callback(session, session->socket);
-
- ssh_packet_set_default_callbacks(session);
- set_status(session, 0.5f);
- session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
- if (ssh_send_kex(session, 1) < 0) {
- goto error;
- }
- break;
- case SSH_SESSION_STATE_INITIAL_KEX:
- /* TODO: This state should disappear in favor of get_key handle */
- break;
- case SSH_SESSION_STATE_KEXINIT_RECEIVED:
- set_status(session,0.6f);
- if(session->next_crypto->server_kex.methods[0]==NULL){
- if(server_set_kex(session) == SSH_ERROR)
- goto error;
- /* We are in a rekeying, so we need to send the server kex */
- if(ssh_send_kex(session, 1) < 0)
- goto error;
- }
- ssh_list_kex(&session->next_crypto->client_kex); // log client kex
- if (ssh_kex_select_methods(session) < 0) {
+ rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
+ if (rc != SSH_OK) {
goto error;
}
- if (crypt_set_algorithms_server(session) == SSH_ERROR)
- goto error;
- set_status(session,0.8f);
- session->session_state=SSH_SESSION_STATE_DH;
- break;
- case SSH_SESSION_STATE_DH:
- if(session->dh_handshake_state==DH_STATE_FINISHED){
-
- rc = ssh_packet_set_newkeys(session, SSH_DIRECTION_IN);
- if (rc != SSH_OK) {
- goto error;
- }
+ /*
+ * If the client supports extension negotiation, we will send
+ * our supported extensions now. This is the first message after
+ * sending NEWKEYS message and after turning on crypto.
+ */
+ if (session->extensions & SSH_EXT_NEGOTIATION &&
+ session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
/*
- * If the client supports extension negotiation, we will send
- * our supported extensions now. This is the first message after
- * sending NEWKEYS message and after turning on crypto.
+ * Only send an SSH_MSG_EXT_INFO message the first time the
+ * client undergoes NEWKEYS. It is unexpected for this message
+ * to be sent upon rekey, and may cause clients to log error
+ * messages.
+ *
+ * The session_state can not be used for this purpose because it
+ * is re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey.
+ * So, use the connected flag which transitions from non-zero
+ * below.
+ *
+ * See also:
+ * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929
*/
- if (session->extensions & SSH_EXT_NEGOTIATION &&
- session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
-
- /*
- * Only send an SSH_MSG_EXT_INFO message the first time the client
- * undergoes NEWKEYS. It is unexpected for this message to be sent
- * upon rekey, and may cause clients to log error messages.
- *
- * The session_state can not be used for this purpose because it is
- * re-set to SSH_SESSION_STATE_KEXINIT_RECEIVED during rekey. So,
- * use the connected flag which transitions from non-zero below.
- *
- * See also:
- * - https://bugzilla.mindrot.org/show_bug.cgi?id=2929
- */
- if (session->connected == 0) {
- ssh_server_send_extensions(session);
- }
+ if (session->connected == 0) {
+ ssh_server_send_extensions(session);
}
+ }
- set_status(session,1.0f);
- session->connected = 1;
- session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
- session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
+ set_status(session, 1.0f);
+ session->connected = 1;
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATING;
+ if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
+ session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
- }
- break;
- case SSH_SESSION_STATE_AUTHENTICATING:
- break;
- case SSH_SESSION_STATE_ERROR:
- goto error;
- default:
- ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
+ }
+ break;
+ case SSH_SESSION_STATE_AUTHENTICATING:
+ break;
+ case SSH_SESSION_STATE_ERROR:
+ goto error;
+ default:
+ ssh_set_error(session, SSH_FATAL, "Invalid state %d",
+ session->session_state);
}
return;
error:
ssh_socket_close(session->socket);
session->alive = 0;
- session->session_state=SSH_SESSION_STATE_ERROR;
+ session->session_state = SSH_SESSION_STATE_ERROR;
}
/**
@@ -459,16 +468,17 @@ error:
* @param user is a pointer to session
* @returns Number of bytes processed, or zero if the banner is not complete.
*/
-static int callback_receive_banner(const void *data, size_t len, void *user) {
- char *buffer = (char *) data;
- ssh_session session = (ssh_session) user;
+static size_t callback_receive_banner(const void *data, size_t len, void *user)
+{
+ char *buffer = (char *)data;
+ ssh_session session = (ssh_session)user;
char *str = NULL;
size_t i;
- int ret=0;
+ size_t processed = 0;
for (i = 0; i < len; i++) {
#ifdef WITH_PCAP
- if(session->pcap_ctx && buffer[i] == '\n') {
+ if (session->pcap_ctx && buffer[i] == '\n') {
ssh_pcap_context_write(session->pcap_ctx,
SSH_PCAP_DIR_IN,
buffer,
@@ -477,33 +487,34 @@ static int callback_receive_banner(const void *data, size_t len, void *user) {
}
#endif
if (buffer[i] == '\r') {
- buffer[i]='\0';
+ buffer[i] = '\0';
}
if (buffer[i] == '\n') {
- buffer[i]='\0';
+ buffer[i] = '\0';
str = strdup(buffer);
/* number of bytes read */
- ret = i + 1;
+ processed = i + 1;
session->clientbanner = str;
session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED;
SSH_LOG(SSH_LOG_PACKET, "Received banner: %s", str);
session->ssh_connection_callback(session);
- return ret;
+ return processed;
}
- if(i > 127) {
+ if (i > 127) {
/* Too big banner */
session->session_state = SSH_SESSION_STATE_ERROR;
- ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner");
+ ssh_set_error(session, SSH_FATAL,
+ "Receiving banner: too large banner");
return 0;
}
}
- return ret;
+ return processed;
}
/* returns 0 until the key exchange is not finished */
@@ -524,11 +535,40 @@ void ssh_set_auth_methods(ssh_session session, int auth_methods)
session->auth.supported_methods = (uint32_t)auth_methods & 0x3fU;
}
+int ssh_send_issue_banner(ssh_session session, const ssh_string banner)
+{
+ int rc = SSH_ERROR;
+
+ if (session == NULL) {
+ return SSH_ERROR;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Sending a server issue banner");
+
+ rc = ssh_buffer_pack(session->out_buffer,
+ "bSs",
+ SSH2_MSG_USERAUTH_BANNER,
+ banner,
+ "");
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
+
+ rc = ssh_packet_send(session);
+ return rc;
+}
+
/* Do the banner and key exchange */
-int ssh_handle_key_exchange(ssh_session session) {
+int ssh_handle_key_exchange(ssh_session session)
+{
int rc;
- if (session->session_state != SSH_SESSION_STATE_NONE)
- goto pending;
+
+ if (session->session_state != SSH_SESSION_STATE_NONE) {
+ goto pending;
+ }
+
rc = ssh_send_banner(session, 1);
if (rc < 0) {
return SSH_ERROR;
@@ -539,27 +579,28 @@ int ssh_handle_key_exchange(ssh_session session) {
session->ssh_connection_callback = ssh_server_connection_callback;
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
ssh_socket_set_callbacks(session->socket,&session->socket_callbacks);
- session->socket_callbacks.data=callback_receive_banner;
- session->socket_callbacks.exception=ssh_socket_exception_callback;
- session->socket_callbacks.userdata=session;
+ session->socket_callbacks.data = callback_receive_banner;
+ session->socket_callbacks.exception = ssh_socket_exception_callback;
+ session->socket_callbacks.userdata = session;
rc = server_set_kex(session);
if (rc < 0) {
return SSH_ERROR;
}
- pending:
+pending:
rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER,
- ssh_server_kex_termination,session);
+ ssh_server_kex_termination,session);
SSH_LOG(SSH_LOG_PACKET, "ssh_handle_key_exchange: current state : %d",
- session->session_state);
- if (rc != SSH_OK)
- return rc;
+ session->session_state);
+ if (rc != SSH_OK) {
+ return rc;
+ }
if (session->session_state == SSH_SESSION_STATE_ERROR ||
session->session_state == SSH_SESSION_STATE_DISCONNECTED) {
- return SSH_ERROR;
+ return SSH_ERROR;
}
- return SSH_OK;
+ return SSH_OK;
}
/* messages */
@@ -648,7 +689,7 @@ static int ssh_message_channel_request_reply_default(ssh_message msg) {
channel = msg->channel_request.channel->remote_channel;
SSH_LOG(SSH_LOG_PACKET,
- "Sending a default channel_request denied to channel %d", channel);
+ "Sending a default channel_request denied to channel %" PRIu32, channel);
rc = ssh_buffer_pack(msg->session->out_buffer,
"bd",
@@ -672,6 +713,13 @@ static int ssh_message_service_request_reply_default(ssh_message msg) {
return ssh_message_service_reply_success(msg);
}
+/**
+ * @brief Sends SERVICE_ACCEPT to the client
+ *
+ * @param msg The message to reply to
+ *
+ * @returns SSH_OK when success otherwise SSH_ERROR
+ */
int ssh_message_service_reply_success(ssh_message msg) {
ssh_session session;
int rc;
@@ -696,6 +744,15 @@ int ssh_message_service_reply_success(ssh_message msg) {
return rc;
}
+/**
+ * @brief Send a global request success message
+ *
+ * @param msg The message
+ *
+ * @param bound_port The remote bind port
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) {
int rc;
@@ -707,7 +764,7 @@ int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_por
goto error;
}
- if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
+ if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
&& msg->global_request.bind_port == 0) {
rc = ssh_buffer_pack(msg->session->out_buffer, "d", bound_port);
if (rc != SSH_OK) {
@@ -719,7 +776,7 @@ int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_por
return ssh_packet_send(msg->session);
}
- if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
+ if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD
&& msg->global_request.bind_port == 0) {
SSH_LOG(SSH_LOG_PACKET,
"The client doesn't want to know the remote port!");
@@ -774,6 +831,13 @@ int ssh_message_reply_default(ssh_message msg) {
return -1;
}
+/**
+ * @brief Gets the service name from the service request message
+ *
+ * @param msg The service request message
+ *
+ * @returns the service name from the message
+ */
const char *ssh_message_service_service(ssh_message msg){
if (msg == NULL) {
return NULL;
@@ -805,7 +869,6 @@ ssh_key ssh_message_auth_pubkey(ssh_message msg) {
return msg->auth_request.pubkey;
}
-/* Get the publickey of an auth request */
ssh_public_key ssh_message_auth_publickey(ssh_message msg){
if (msg == NULL) {
return NULL;
@@ -821,6 +884,13 @@ enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){
return msg->auth_request.signature_state;
}
+/**
+ * @brief Check if the message is a keyboard-interactive response
+ *
+ * @param msg The message to check
+ *
+ * @returns 1 if the message is a response, otherwise 0
+ */
int ssh_message_auth_kbdint_is_response(ssh_message msg) {
if (msg == NULL) {
return -1;
@@ -830,6 +900,17 @@ int ssh_message_auth_kbdint_is_response(ssh_message msg) {
}
/* FIXME: methods should be unsigned */
+/**
+ * @brief Sets the supported authentication methods to a message
+ *
+ * @param msg The message
+ *
+ * @param methods Methods to set to the message.
+ * The supported methods are listed in ssh_set_auth_methods
+ * @see ssh_set_auth_methods
+ *
+ * @returns 0 on success, otherwise -1
+ */
int ssh_message_auth_set_methods(ssh_message msg, int methods) {
if (msg == NULL || msg->session == NULL) {
return -1;
@@ -884,9 +965,8 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
/* fill in the kbdint structure */
if (msg->session->kbdint == NULL) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Warning: Got a "
- "keyboard-interactive response but it "
- "seems we didn't send the request.");
+ SSH_LOG(SSH_LOG_DEBUG, "Warning: Got a keyboard-interactive response "
+ "but it seems we didn't send the request.");
msg->session->kbdint = ssh_kbdint_new();
if (msg->session->kbdint == NULL) {
@@ -950,6 +1030,17 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
return rc;
}
+/**
+ * @brief Sends SSH2_MSG_USERAUTH_SUCCESS or SSH2_MSG_USERAUTH_FAILURE message
+ * depending on the success of the authentication method
+ *
+ * @param session The session to reply to
+ *
+ * @param partial Denotes if the authentication process was partially completed
+ * (unsuccessful)
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_auth_reply_success(ssh_session session, int partial)
{
struct ssh_crypto_struct *crypto = NULL;
@@ -981,13 +1072,13 @@ int ssh_auth_reply_success(ssh_session session, int partial)
crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT);
if (crypto != NULL && crypto->delayed_compress_out) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Enabling delayed compression OUT");
+ SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT");
crypto->do_compress_out = 1;
}
crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN);
if (crypto != NULL && crypto->delayed_compress_in) {
- SSH_LOG(SSH_LOG_PROTOCOL, "Enabling delayed compression IN");
+ SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN");
crypto->do_compress_in = 1;
}
return r;
@@ -999,7 +1090,17 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) {
return ssh_auth_reply_success(msg->session, partial);
}
-/* Answer OK to a pubkey auth request */
+/**
+ * @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
+ *
+ * @param msg The message
+ *
+ * @param algo The algorithm of the accepted public key
+ *
+ * @param pubkey The accepted public key
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) {
int rc;
if (msg == NULL) {
@@ -1020,12 +1121,19 @@ int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pu
return rc;
}
+/**
+ * @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
+ *
+ * @param msg The message
+ *
+ * @returns SSH_OK on success, otherwise SSH_ERROR
+ */
int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) {
ssh_string algo;
ssh_string pubkey_blob = NULL;
int ret;
- algo = ssh_string_from_char(msg->auth_request.pubkey->type_c);
+ algo = ssh_string_from_char(msg->auth_request.sigtype);
if (algo == NULL) {
return SSH_ERROR;
}
@@ -1170,6 +1278,13 @@ int ssh_execute_message_callbacks(ssh_session session){
return SSH_OK;
}
+/**
+ * @brief Sends a keepalive message to the session
+ *
+ * @param session The session to send the message to
+ *
+ * @returns SSH_OK
+ */
int ssh_send_keepalive(ssh_session session)
{
/* Client denies the request, so the error code is not meaningful */
diff --git a/src/session.c b/src/session.c
index 3199096a..279352b6 100644
--- a/src/session.c
+++ b/src/session.c
@@ -43,7 +43,7 @@
#define FIRST_CHANNEL 42 // why not ? it helps to find bugs.
/**
- * @defgroup libssh_session The SSH session functions.
+ * @defgroup libssh_session The SSH session functions
* @ingroup libssh
*
* Functions that manage a session.
@@ -97,36 +97,53 @@ ssh_session ssh_new(void)
ssh_set_blocking(session, 1);
session->maxchannel = FIRST_CHANNEL;
-#ifndef _WIN32
session->agent = ssh_agent_new(session);
if (session->agent == NULL) {
goto err;
}
-#endif /* _WIN32 */
/* OPTIONS */
session->opts.StrictHostKeyChecking = 1;
- session->opts.port = 0;
+ session->opts.port = 22;
session->opts.fd = -1;
session->opts.compressionlevel = 7;
session->opts.nodelay = 0;
+ session->opts.identities_only = false;
+ session->opts.control_master = SSH_CONTROL_MASTER_NO;
session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH |
SSH_OPT_FLAG_PUBKEY_AUTH |
SSH_OPT_FLAG_KBDINT_AUTH |
SSH_OPT_FLAG_GSSAPI_AUTH;
+ session->opts.exp_flags = 0;
+
session->opts.identity = ssh_list_new();
if (session->opts.identity == NULL) {
goto err;
}
+ session->opts.identity_non_exp = ssh_list_new();
+ if (session->opts.identity_non_exp == NULL) {
+ goto err;
+ }
+
+ session->opts.certificate = ssh_list_new();
+ if (session->opts.certificate == NULL) {
+ goto err;
+ }
+ session->opts.certificate_non_exp = ssh_list_new();
+ if (session->opts.certificate_non_exp == NULL) {
+ goto err;
+ }
+ /* the default certificates are loaded automatically from the default
+ * identities later */
id = strdup("%d/id_ed25519");
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -136,7 +153,7 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
@@ -146,22 +163,11 @@ ssh_session ssh_new(void)
if (id == NULL) {
goto err;
}
- rc = ssh_list_append(session->opts.identity, id);
+ rc = ssh_list_append(session->opts.identity_non_exp, id);
if (rc == SSH_ERROR) {
goto err;
}
-#ifdef HAVE_DSA
- id = strdup("%d/id_dsa");
- if (id == NULL) {
- goto err;
- }
- rc = ssh_list_append(session->opts.identity, id);
- if (rc == SSH_ERROR) {
- goto err;
- }
-#endif
-
/* Explicitly initialize states */
session->session_state = SSH_SESSION_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
@@ -242,12 +248,8 @@ void ssh_free(ssh_session session)
crypto_free(session->current_crypto);
crypto_free(session->next_crypto);
-#ifndef _WIN32
ssh_agent_free(session->agent);
-#endif /* _WIN32 */
- ssh_key_free(session->srv.dsa_key);
- session->srv.dsa_key = NULL;
ssh_key_free(session->srv.rsa_key);
session->srv.rsa_key = NULL;
ssh_key_free(session->srv.ecdsa_key);
@@ -286,24 +288,59 @@ void ssh_free(ssh_session session)
ssh_list_free(session->opts.identity);
}
+ if (session->opts.identity_non_exp) {
+ char *id;
+
+ for (id = ssh_list_pop_head(char *, session->opts.identity_non_exp);
+ id != NULL;
+ id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) {
+ SAFE_FREE(id);
+ }
+ ssh_list_free(session->opts.identity_non_exp);
+ }
+
+ if (session->opts.certificate) {
+ char *cert = NULL;
+
+ for (cert = ssh_list_pop_head(char *, session->opts.certificate);
+ cert != NULL;
+ cert = ssh_list_pop_head(char *, session->opts.certificate)) {
+ SAFE_FREE(cert);
+ }
+ ssh_list_free(session->opts.certificate);
+ }
+
+ if (session->opts.certificate_non_exp) {
+ char *cert = NULL;
+
+ for (cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp);
+ cert != NULL;
+ cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) {
+ SAFE_FREE(cert);
+ }
+ ssh_list_free(session->opts.certificate_non_exp);
+ }
+
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
session->out_queue)) != NULL) {
SSH_BUFFER_FREE(b);
}
ssh_list_free(session->out_queue);
-#ifndef _WIN32
- ssh_agent_state_free (session->agent_state);
-#endif
+ ssh_agent_state_free(session->agent_state);
session->agent_state = NULL;
SAFE_FREE(session->auth.auto_state);
SAFE_FREE(session->serverbanner);
SAFE_FREE(session->clientbanner);
SAFE_FREE(session->banner);
+ SAFE_FREE(session->disconnect_message);
+ SAFE_FREE(session->peer_discon_msg);
+ SAFE_FREE(session->opts.agent_socket);
SAFE_FREE(session->opts.bindaddr);
SAFE_FREE(session->opts.custombanner);
+ SAFE_FREE(session->opts.moduli_file);
SAFE_FREE(session->opts.username);
SAFE_FREE(session->opts.host);
SAFE_FREE(session->opts.sshdir);
@@ -313,6 +350,7 @@ void ssh_free(ssh_session session)
SAFE_FREE(session->opts.gss_server_identity);
SAFE_FREE(session->opts.gss_client_identity);
SAFE_FREE(session->opts.pubkey_accepted_types);
+ SAFE_FREE(session->opts.control_path);
for (i = 0; i < SSH_KEX_METHODS; i++) {
if (session->opts.wanted_methods[i]) {
@@ -458,20 +496,36 @@ const char* ssh_get_hmac_out(ssh_session session) {
}
/**
+ * @internal
+ * @brief Close the connection socket if it is a socket created by us.
+ * Does not close the sockets provided by the user through options API.
+ */
+void
+ssh_session_socket_close(ssh_session session)
+{
+ if (session->opts.fd == SSH_INVALID_SOCKET) {
+ ssh_socket_close(session->socket);
+ }
+ session->alive = 0;
+ session->session_state = SSH_SESSION_STATE_ERROR;
+}
+
+/**
* @brief Disconnect impolitely from a remote host by closing the socket.
*
* Suitable if you forked and want to destroy this session.
*
* @param[in] session The SSH session to disconnect.
*/
-void ssh_silent_disconnect(ssh_session session) {
- if (session == NULL) {
- return;
- }
+void
+ssh_silent_disconnect(ssh_session session)
+{
+ if (session == NULL) {
+ return;
+ }
- ssh_socket_close(session->socket);
- session->alive = 0;
- ssh_disconnect(session);
+ ssh_session_socket_close(session);
+ ssh_disconnect(session);
}
/**
@@ -632,9 +686,10 @@ void ssh_set_fd_except(ssh_session session) {
*
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
-int ssh_handle_packets(ssh_session session, int timeout) {
- ssh_poll_handle spoll;
- ssh_poll_ctx ctx;
+int ssh_handle_packets(ssh_session session, int timeout)
+{
+ ssh_poll_handle spoll = NULL;
+ ssh_poll_ctx ctx = NULL;
int tm = timeout;
int rc;
@@ -643,20 +698,29 @@ int ssh_handle_packets(ssh_session session, int timeout) {
}
spoll = ssh_socket_get_poll_handle(session->socket);
+ if (spoll == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
ssh_poll_add_events(spoll, POLLIN);
ctx = ssh_poll_get_ctx(spoll);
- if (!ctx) {
+ if (ctx == NULL) {
ctx = ssh_poll_get_default_ctx(session);
+ if (ctx == NULL) {
+ ssh_set_error_oom(session);
+ return SSH_ERROR;
+ }
ssh_poll_ctx_add(ctx, spoll);
}
if (timeout == SSH_TIMEOUT_USER) {
- if (ssh_is_blocking(session))
- tm = ssh_make_milliseconds(session->opts.timeout,
- session->opts.timeout_usec);
- else
- tm = 0;
+ if (ssh_is_blocking(session)) {
+ tm = ssh_make_milliseconds(session->opts.timeout,
+ session->opts.timeout_usec);
+ } else {
+ tm = 0;
+ }
}
rc = ssh_poll_ctx_dopoll(ctx, tm);
if (rc == SSH_ERROR) {
@@ -679,7 +743,7 @@ int ssh_handle_packets(ssh_session session, int timeout) {
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying
* SSH_TIMEOUT_INFINITE (-1) means an infinite timeout.
- * Specifying SSH_TIMEOUT_USER means to use the timeout
+ * Specifying SSH_TIMEOUT_USER means using the timeout
* specified in options. 0 means poll will return
* immediately.
* SSH_TIMEOUT_DEFAULT uses the session timeout if set or
@@ -693,13 +757,13 @@ int ssh_handle_packets(ssh_session session, int timeout) {
* SSH_ERROR otherwise.
*/
int ssh_handle_packets_termination(ssh_session session,
- long timeout,
+ int timeout,
ssh_termination_function fct,
void *user)
{
struct ssh_timestamp ts;
- long timeout_ms = SSH_TIMEOUT_INFINITE;
- long tm;
+ int timeout_ms = SSH_TIMEOUT_INFINITE;
+ int tm;
int ret = SSH_OK;
/* If a timeout has been provided, use it */
@@ -818,11 +882,11 @@ const char *ssh_get_disconnect_message(ssh_session session) {
if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Connection not closed yet");
- } else if(!session->discon_msg) {
+ } else if(!session->peer_discon_msg) {
ssh_set_error(session, SSH_FATAL,
"Connection correctly closed but no disconnect message");
} else {
- return session->discon_msg;
+ return session->peer_discon_msg;
}
return NULL;
@@ -856,7 +920,9 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){
if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) {
ssh_set_error(session, SSH_FATAL, "Socket error: disconnected");
} else {
- ssh_set_error(session, SSH_FATAL, "Socket error: %s", strerror(errno_code));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ ssh_set_error(session, SSH_FATAL, "Socket error: %s",
+ ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX));
}
session->ssh_connection_callback(session);
@@ -933,7 +999,7 @@ error:
/**
* @brief Set the session data counters.
*
- * This functions sets the counter structures to be used to calculate data
+ * This function sets the counter structures to be used to calculate data
* which comes in and goes out through the session at different levels.
*
* @code
@@ -976,8 +1042,8 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
{
ssh_key pubkey = NULL;
ssh_string pubkey_blob = NULL;
- MD5CTX ctx;
- unsigned char *h;
+ MD5CTX ctx = NULL;
+ unsigned char *h = NULL;
int rc;
if (session == NULL || hash == NULL) {
@@ -997,40 +1063,50 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
*hash = NULL;
if (session->current_crypto == NULL ||
session->current_crypto->server_pubkey == NULL) {
- ssh_set_error(session,SSH_FATAL,"No current cryptographic context");
+ ssh_set_error(session, SSH_FATAL, "No current cryptographic context");
+ return SSH_ERROR;
+ }
+
+ rc = ssh_get_server_publickey(session, &pubkey);
+ if (rc != SSH_OK) {
+ return SSH_ERROR;
+ }
+
+ rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
+ ssh_key_free(pubkey);
+ if (rc != SSH_OK) {
return SSH_ERROR;
}
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
if (h == NULL) {
+ SSH_STRING_FREE(pubkey_blob);
return SSH_ERROR;
}
ctx = md5_init();
if (ctx == NULL) {
+ SSH_STRING_FREE(pubkey_blob);
SAFE_FREE(h);
return SSH_ERROR;
}
- rc = ssh_get_server_publickey(session, &pubkey);
+ rc = md5_update(ctx,
+ ssh_string_data(pubkey_blob),
+ ssh_string_len(pubkey_blob));
if (rc != SSH_OK) {
- md5_final(h, ctx);
+ SSH_STRING_FREE(pubkey_blob);
+ md5_ctx_free(ctx);
SAFE_FREE(h);
- return SSH_ERROR;
+ return rc;
}
-
- rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
- ssh_key_free(pubkey);
+ SSH_STRING_FREE(pubkey_blob);
+ rc = md5_final(h, ctx);
if (rc != SSH_OK) {
- md5_final(h, ctx);
SAFE_FREE(h);
- return SSH_ERROR;
+ return rc;
}
- md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob));
- SSH_STRING_FREE(pubkey_blob);
- md5_final(h, ctx);
-
*hash = h;
return MD5_DIGEST_LEN;
@@ -1039,14 +1115,15 @@ int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
/**
* @brief Deallocate the hash obtained by ssh_get_pubkey_hash.
*
- * This is required under Microsoft platform as this library might use a
+ * This is required under Microsoft platform as this library might use a
* different C library than your software, hence a different heap.
*
* @param[in] hash The buffer to deallocate.
*
* @see ssh_get_pubkey_hash()
*/
-void ssh_clean_pubkey_hash(unsigned char **hash) {
+void ssh_clean_pubkey_hash(unsigned char **hash)
+{
SAFE_FREE(*hash);
}
@@ -1056,9 +1133,9 @@ void ssh_clean_pubkey_hash(unsigned char **hash) {
* @param[in] session The session to get the key from.
*
* @param[out] key A pointer to store the allocated key. You need to free
- * the key.
+ * the key using ssh_key_free().
*
- * @return SSH_OK on success, SSH_ERROR on errror.
+ * @return SSH_OK on success, SSH_ERROR on error.
*
* @see ssh_key_free()
*/
@@ -1100,15 +1177,15 @@ int ssh_get_publickey(ssh_session session, ssh_key *key)
*
* @param[in] type The type of the hash you want.
*
- * @param[in] hash A pointer to store the allocated buffer. It can be
+ * @param[out] hash A pointer to store the allocated buffer. It can be
* freed using ssh_clean_pubkey_hash().
*
* @param[in] hlen The length of the hash.
*
- * @return 0 on success, -1 if an error occured.
+ * @return 0 on success, -1 if an error occurred.
*
* @warning It is very important that you verify at some moment that the hash
- * matches a known server. If you don't do it, cryptography wont help
+ * matches a known server. If you don't do it, cryptography won't help
* you at making things secure.
* OpenSSH uses SHA256 to print public key digests.
*
@@ -1123,7 +1200,7 @@ int ssh_get_publickey_hash(const ssh_key key,
size_t *hlen)
{
ssh_string blob;
- unsigned char *h;
+ unsigned char *h = NULL;
int rc;
rc = ssh_pki_export_pubkey_blob(key, &blob);
@@ -1149,8 +1226,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- sha1_final(h, ctx);
+ rc = sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ sha1_ctx_free(ctx);
+ goto out;
+ }
+ rc = sha1_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = SHA_DIGEST_LEN;
}
@@ -1172,8 +1258,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- sha256_final(h, ctx);
+ rc = sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ sha256_ctx_free(ctx);
+ goto out;
+ }
+ rc = sha256_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = SHA256_DIGEST_LEN;
}
@@ -1184,8 +1279,8 @@ int ssh_get_publickey_hash(const ssh_key key,
/* In FIPS mode, we cannot use MD5 */
if (ssh_fips_mode()) {
- SSH_LOG(SSH_LOG_WARN, "In FIPS mode MD5 is not allowed."
- "Try using SSH_PUBLICKEY_HASH_SHA256");
+ SSH_LOG(SSH_LOG_TRACE, "In FIPS mode MD5 is not allowed."
+ "Try using SSH_PUBLICKEY_HASH_SHA256");
rc = SSH_ERROR;
goto out;
}
@@ -1203,8 +1298,17 @@ int ssh_get_publickey_hash(const ssh_key key,
goto out;
}
- md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
- md5_final(h, ctx);
+ rc = md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
+ if (rc != SSH_OK) {
+ free(h);
+ md5_ctx_free(ctx);
+ goto out;
+ }
+ rc = md5_final(h, ctx);
+ if (rc != SSH_OK) {
+ free(h);
+ goto out;
+ }
*hlen = MD5_DIGEST_LEN;
}
diff --git a/src/sftp.c b/src/sftp.c
index 07cb1c0e..76a9e13e 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -32,6 +32,9 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
@@ -53,22 +56,12 @@
#ifdef WITH_SFTP
-/* Buffer size maximum is 256M */
-#define SFTP_PACKET_SIZE_MAX 0x10000000
-#define SFTP_BUFFER_SIZE_MAX 16384
-
struct sftp_ext_struct {
uint32_t count;
char **name;
char **data;
};
-/* functions */
-static int sftp_enqueue(sftp_session session, sftp_message msg);
-static void sftp_message_free(sftp_message msg);
-static void sftp_set_error(sftp_session sftp, int errnum);
-static void status_msg_free(sftp_status_message status);
-
static sftp_ext sftp_ext_new(void) {
sftp_ext ext;
@@ -173,131 +166,129 @@ error:
return NULL;
}
-sftp_session sftp_new_channel(ssh_session session, ssh_channel channel){
- sftp_session sftp;
+sftp_session
+sftp_new_channel(ssh_session session, ssh_channel channel)
+{
+ sftp_session sftp = NULL;
- if (session == NULL) {
- return NULL;
- }
+ if (session == NULL) {
+ return NULL;
+ }
- sftp = calloc(1, sizeof(struct sftp_session_struct));
- if (sftp == NULL) {
- ssh_set_error_oom(session);
+ sftp = calloc(1, sizeof(struct sftp_session_struct));
+ if (sftp == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
- return NULL;
- }
+ sftp->ext = sftp_ext_new();
+ if (sftp->ext == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- sftp->ext = sftp_ext_new();
- if (sftp->ext == NULL) {
- ssh_set_error_oom(session);
- SAFE_FREE(sftp);
+ sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct));
+ if (sftp->read_packet == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- return NULL;
- }
+ sftp->read_packet->payload = ssh_buffer_new();
+ if (sftp->read_packet->payload == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- sftp->session = session;
- sftp->channel = channel;
+ sftp->session = session;
+ sftp->channel = channel;
- return sftp;
+ return sftp;
+
+error:
+ if (sftp->ext != NULL) {
+ sftp_ext_free(sftp->ext);
+ }
+ if (sftp->read_packet != NULL) {
+ if (sftp->read_packet->payload != NULL) {
+ SSH_BUFFER_FREE(sftp->read_packet->payload);
+ }
+ SAFE_FREE(sftp->read_packet);
+ }
+ SAFE_FREE(sftp);
+ return NULL;
}
#ifdef WITH_SERVER
-sftp_session sftp_server_new(ssh_session session, ssh_channel chan){
- sftp_session sftp = NULL;
+sftp_session
+sftp_server_new(ssh_session session, ssh_channel chan)
+{
+ sftp_session sftp = NULL;
- sftp = calloc(1, sizeof(struct sftp_session_struct));
- if (sftp == NULL) {
- ssh_set_error_oom(session);
- return NULL;
- }
+ sftp = calloc(1, sizeof(struct sftp_session_struct));
+ if (sftp == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
- sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct));
- if (sftp->read_packet == NULL) {
- goto error;
- }
+ sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct));
+ if (sftp->read_packet == NULL) {
+ goto error;
+ }
- sftp->read_packet->payload = ssh_buffer_new();
- if (sftp->read_packet->payload == NULL) {
- goto error;
- }
+ sftp->read_packet->payload = ssh_buffer_new();
+ if (sftp->read_packet->payload == NULL) {
+ goto error;
+ }
- sftp->session = session;
- sftp->channel = chan;
+ sftp->session = session;
+ sftp->channel = chan;
- return sftp;
+ return sftp;
error:
- ssh_set_error_oom(session);
- if (sftp->read_packet != NULL) {
- if (sftp->read_packet->payload != NULL) {
- SSH_BUFFER_FREE(sftp->read_packet->payload);
+ ssh_set_error_oom(session);
+ if (sftp->read_packet != NULL) {
+ if (sftp->read_packet->payload != NULL) {
+ SSH_BUFFER_FREE(sftp->read_packet->payload);
+ }
+ SAFE_FREE(sftp->read_packet);
}
- SAFE_FREE(sftp->read_packet);
- }
- SAFE_FREE(sftp);
- return NULL;
+ SAFE_FREE(sftp);
+ return NULL;
}
-int sftp_server_init(sftp_session sftp){
- ssh_session session = sftp->session;
- sftp_packet packet = NULL;
- ssh_buffer reply = NULL;
- uint32_t version;
- int rc;
-
- packet = sftp_packet_read(sftp);
- if (packet == NULL) {
- return -1;
- }
-
- if (packet->type != SSH_FXP_INIT) {
- ssh_set_error(session, SSH_FATAL,
- "Packet read of type %d instead of SSH_FXP_INIT",
- packet->type);
-
- return -1;
- }
-
- SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT");
-
- ssh_buffer_get_u32(packet->payload, &version);
- version = ntohl(version);
- SSH_LOG(SSH_LOG_PACKET, "Client version: %d", version);
- sftp->client_version = (int)version;
-
- reply = ssh_buffer_new();
- if (reply == NULL) {
- ssh_set_error_oom(session);
- return -1;
- }
+/* @deprecated in favor of sftp_server_new() and callbacks based sftp server */
+int sftp_server_init(sftp_session sftp)
+{
+ ssh_session session = sftp->session;
+ sftp_client_message msg = NULL;
+ int rc;
- rc = ssh_buffer_pack(reply, "dssss",
- LIBSFTP_VERSION,
- "posix-rename@openssh.com",
- "1",
- "hardlink@openssh.com",
- "1");
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- SSH_BUFFER_FREE(reply);
- return -1;
- }
+ /* handles setting the sftp->client_version */
+ msg = sftp_get_client_message(sftp);
+ if (msg == NULL) {
+ return -1;
+ }
- if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) {
- SSH_BUFFER_FREE(reply);
- return -1;
- }
- SSH_BUFFER_FREE(reply);
+ if (msg->type != SSH_FXP_INIT) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Packet read of type %d instead of SSH_FXP_INIT",
+ msg->type);
+ return -1;
+ }
- SSH_LOG(SSH_LOG_PROTOCOL, "Server version sent");
+ SSH_LOG(SSH_LOG_PACKET, "Received SSH_FXP_INIT");
- if (version > LIBSFTP_VERSION) {
- sftp->version = LIBSFTP_VERSION;
- } else {
- sftp->version = (int)version;
- }
+ rc = sftp_reply_version(msg);
+ if (rc != SSH_OK) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Failed to process the SSH_FXP_INIT message");
+ return -1;
+ }
- return 0;
+ return 0;
}
void sftp_server_free(sftp_session sftp)
@@ -325,6 +316,7 @@ void sftp_server_free(sftp_session sftp)
SAFE_FREE(sftp);
}
+
#endif /* WITH_SERVER */
void sftp_free(sftp_session sftp)
@@ -355,176 +347,62 @@ void sftp_free(sftp_session sftp)
SAFE_FREE(sftp->read_packet);
sftp_ext_free(sftp->ext);
+ sftp_limits_free(sftp->limits);
SAFE_FREE(sftp);
}
-ssize_t sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
-{
- uint8_t header[5] = {0};
- uint32_t payload_size;
- ssize_t size;
- int rc;
-
- /* Add size of type */
- payload_size = ssh_buffer_get_len(payload) + sizeof(uint8_t);
- PUSH_BE_U32(header, 0, payload_size);
- PUSH_BE_U8(header, 4, type);
-
- rc = ssh_buffer_prepend_data(payload, header, sizeof(header));
- if (rc < 0) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
-
- size = ssh_channel_write(sftp->channel,
- ssh_buffer_get(payload),
- ssh_buffer_get_len(payload));
- if (size < 0) {
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
-
- if ((uint32_t)size != ssh_buffer_get_len(payload)) {
- SSH_LOG(SSH_LOG_PACKET,
- "Had to write %d bytes, wrote only %zd",
- ssh_buffer_get_len(payload),
- size);
- }
-
- return size;
-}
-
-sftp_packet sftp_packet_read(sftp_session sftp)
+/* @internal
+ * Process the incoming data and copy them from the SSH packet buffer to the
+ * SFTP packet buffer.
+ * @returns number of decoded bytes.
+ */
+int
+sftp_decode_channel_data_to_packet(sftp_session sftp, void *data, uint32_t len)
{
- uint8_t buffer[SFTP_BUFFER_SIZE_MAX];
sftp_packet packet = sftp->read_packet;
- uint32_t size;
int nread;
- bool is_eof;
- int rc;
+ int payload_len;
+ unsigned int data_offset;
+ int to_read, rc;
- packet->sftp = sftp;
-
- /*
- * If the packet has a payload, then just reinit the buffer, otherwise
- * allocate a new one.
- */
- if (packet->payload != NULL) {
- rc = ssh_buffer_reinit(packet->payload);
- if (rc != 0) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
- } else {
- packet->payload = ssh_buffer_new();
- if (packet->payload == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
+ if (packet->sftp == NULL) {
+ packet->sftp = sftp;
}
- nread = 0;
- do {
- int s;
-
- // read from channel until 4 bytes have been read or an error occurs
- s = ssh_channel_read(sftp->channel, buffer + nread, 4 - nread, 0);
- if (s < 0) {
- goto error;
- } else if (s == 0) {
- is_eof = ssh_channel_is_eof(sftp->channel);
- if (is_eof) {
- ssh_set_error(sftp->session,
- SSH_FATAL,
- "Received EOF while reading sftp packet size");
- sftp_set_error(sftp, SSH_FX_EOF);
- goto error;
- }
- } else {
- nread += s;
- }
- } while (nread < 4);
-
- size = PULL_BE_U32(buffer, 0);
- if (size == 0 || size > SFTP_PACKET_SIZE_MAX) {
- ssh_set_error(sftp->session, SSH_FATAL, "Invalid sftp packet size!");
- sftp_set_error(sftp, SSH_FX_FAILURE);
- goto error;
+ data_offset = sizeof(uint32_t) + sizeof(uint8_t);
+ /* not enough bytes to read */
+ if (len < data_offset) {
+ return SSH_ERROR;
}
- do {
- nread = ssh_channel_read(sftp->channel, buffer, 1, 0);
- if (nread < 0) {
- goto error;
- } else if (nread == 0) {
- is_eof = ssh_channel_is_eof(sftp->channel);
- if (is_eof) {
- ssh_set_error(sftp->session,
- SSH_FATAL,
- "Received EOF while reading sftp packet type");
- sftp_set_error(sftp, SSH_FX_EOF);
- goto error;
- }
- }
- } while (nread < 1);
-
- packet->type = buffer[0];
+ payload_len = PULL_BE_U32(data, 0);
+ packet->type = PULL_BE_U8(data, 4);
- /* Remove the packet type size */
- size -= sizeof(uint8_t);
-
- nread = ssh_buffer_allocate_size(packet->payload, size);
- if (nread < 0) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- goto error;
+ /* We should check the legality of payload length */
+ if (payload_len + sizeof(uint32_t) > len || payload_len < 0) {
+ return SSH_ERROR;
}
- while (size > 0 && size < SFTP_PACKET_SIZE_MAX) {
- nread = ssh_channel_read(sftp->channel,
- buffer,
- sizeof(buffer) > size ? size : sizeof(buffer),
- 0);
- if (nread < 0) {
- /* TODO: check if there are cases where an error needs to be set here */
- goto error;
- }
-
- if (nread > 0) {
- rc = ssh_buffer_add_data(packet->payload, buffer, nread);
- if (rc != 0) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- goto error;
- }
- } else { /* nread == 0 */
- /* Retry the reading unless the remote was closed */
- is_eof = ssh_channel_is_eof(sftp->channel);
- if (is_eof) {
- ssh_set_error(sftp->session,
- SSH_REQUEST_DENIED,
- "Received EOF while reading sftp packet");
- sftp_set_error(sftp, SSH_FX_EOF);
- goto error;
- }
- }
- size -= nread;
+ to_read = payload_len - sizeof(uint8_t);
+ rc = ssh_buffer_add_data(packet->payload,
+ (void*)((uint8_t *)data + data_offset),
+ to_read);
+ if (rc != 0) {
+ return SSH_ERROR;
}
+ nread = ssh_buffer_get_len(packet->payload);
- return packet;
-error:
- ssh_buffer_reinit(packet->payload);
- return NULL;
-}
+ /* We should check if we copied the whole data */
+ if (nread != to_read) {
+ return SSH_ERROR;
+ }
-static void sftp_set_error(sftp_session sftp, int errnum) {
- if (sftp != NULL) {
- sftp->errnum = errnum;
- }
+ /*
+ * We should return how many bytes we decoded, including packet length header
+ * and the payload length.
+ */
+ return payload_len + sizeof(uint32_t);
}
/* Get the last sftp error */
@@ -536,199 +414,155 @@ int sftp_get_error(sftp_session sftp) {
return sftp->errnum;
}
-static void sftp_message_free(sftp_message msg)
-{
- if (msg == NULL) {
- return;
- }
-
- SSH_BUFFER_FREE(msg->payload);
- SAFE_FREE(msg);
-}
+static sftp_limits_t sftp_limits_use_extension(sftp_session sftp);
+static sftp_limits_t sftp_limits_use_default(sftp_session sftp);
-static sftp_message sftp_get_message(sftp_packet packet)
+/* Initialize the sftp session with the server. */
+int sftp_init(sftp_session sftp)
{
- sftp_session sftp = packet->sftp;
- sftp_message msg = NULL;
+ sftp_packet packet = NULL;
+ ssh_buffer buffer = NULL;
+ char *ext_name = NULL;
+ char *ext_data = NULL;
+ uint32_t version;
int rc;
- switch(packet->type) {
- case SSH_FXP_STATUS:
- case SSH_FXP_HANDLE:
- case SSH_FXP_DATA:
- case SSH_FXP_ATTRS:
- case SSH_FXP_NAME:
- case SSH_FXP_EXTENDED_REPLY:
- break;
- default:
- ssh_set_error(packet->sftp->session,
- SSH_FATAL,
- "Unknown packet type %d",
- packet->type);
- sftp_set_error(packet->sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- msg = calloc(1, sizeof(struct sftp_message_struct));
- if (msg == NULL) {
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
- sftp_set_error(packet->sftp, SSH_FX_FAILURE);
- return NULL;
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
}
- msg->sftp = packet->sftp;
- msg->packet_type = packet->type;
-
- /* Move the payload from the packet to the message */
- msg->payload = packet->payload;
- packet->payload = NULL;
-
- rc = ssh_buffer_unpack(msg->payload, "d", &msg->id);
+ rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION);
if (rc != SSH_OK) {
- ssh_set_error(packet->sftp->session, SSH_FATAL,
- "Invalid packet %d: no ID", packet->type);
- sftp_message_free(msg);
- sftp_set_error(packet->sftp, SSH_FX_FAILURE);
- return NULL;
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
}
- SSH_LOG(SSH_LOG_PACKET,
- "Packet with id %d type %d",
- msg->id,
- msg->packet_type);
-
- return msg;
-}
+ rc = sftp_packet_write(sftp, SSH_FXP_INIT, buffer);
+ if (rc == SSH_ERROR) {
+ SSH_BUFFER_FREE(buffer);
+ return -1;
+ }
-static int sftp_read_and_dispatch(sftp_session sftp)
-{
- sftp_packet packet = NULL;
- sftp_message msg = NULL;
+ SSH_BUFFER_FREE(buffer);
packet = sftp_packet_read(sftp);
if (packet == NULL) {
- /* something nasty happened reading the packet */
return -1;
}
- msg = sftp_get_message(packet);
- if (msg == NULL) {
+ if (packet->type != SSH_FXP_VERSION) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received a %d messages instead of SSH_FXP_VERSION",
+ packet->type);
return -1;
}
- if (sftp_enqueue(sftp, msg) < 0) {
- sftp_message_free(msg);
+ /* TODO: are we sure there are 4 bytes ready? */
+ rc = ssh_buffer_unpack(packet->payload, "d", &version);
+ if (rc != SSH_OK) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Unable to unpack SSH_FXP_VERSION packet");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
return -1;
}
- return 0;
-}
+ SSH_LOG(SSH_LOG_DEBUG,
+ "SFTP server version %" PRIu32,
+ version);
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
+ while (rc == SSH_OK) {
+ uint32_t count = sftp->ext->count;
+ char **tmp;
-void sftp_packet_free(sftp_packet packet)
-{
- if (packet == NULL) {
- return;
- }
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_data);
+ if (rc == SSH_ERROR) {
+ break;
+ }
- SSH_BUFFER_FREE(packet->payload);
- free(packet);
-}
+ SSH_LOG(SSH_LOG_DEBUG,
+ "SFTP server extension: %s, version: %s",
+ ext_name, ext_data);
-/* Initialize the sftp session with the server. */
-int sftp_init(sftp_session sftp) {
- sftp_packet packet = NULL;
- ssh_buffer buffer = NULL;
- char *ext_name = NULL;
- char *ext_data = NULL;
- uint32_t version;
- int rc;
+ count++;
+ tmp = realloc(sftp->ext->name, count * sizeof(char *));
+ if (tmp == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(ext_name);
+ SAFE_FREE(ext_data);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- buffer = ssh_buffer_new();
- if (buffer == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
+ tmp[count - 1] = ext_name;
+ sftp->ext->name = tmp;
- rc = ssh_buffer_pack(buffer, "d", LIBSFTP_VERSION);
- if (rc != SSH_OK) {
- ssh_set_error_oom(sftp->session);
- SSH_BUFFER_FREE(buffer);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
- if (sftp_packet_write(sftp, SSH_FXP_INIT, buffer) < 0) {
- SSH_BUFFER_FREE(buffer);
- return -1;
- }
- SSH_BUFFER_FREE(buffer);
+ tmp = realloc(sftp->ext->data, count * sizeof(char *));
+ if (tmp == NULL) {
+ ssh_set_error_oom(sftp->session);
+ SAFE_FREE(ext_name);
+ SAFE_FREE(ext_data);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- packet = sftp_packet_read(sftp);
- if (packet == NULL) {
- return -1;
- }
+ tmp[count - 1] = ext_data;
+ sftp->ext->data = tmp;
- if (packet->type != SSH_FXP_VERSION) {
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received a %d messages instead of SSH_FXP_VERSION", packet->type);
- return -1;
- }
+ sftp->ext->count = count;
- /* TODO: are we sure there are 4 bytes ready? */
- rc = ssh_buffer_unpack(packet->payload, "d", &version);
- if (rc != SSH_OK){
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SFTP server version %d",
- version);
- rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
- while (rc == SSH_OK) {
- uint32_t count = sftp->ext->count;
- char **tmp;
-
- rc = ssh_buffer_unpack(packet->payload, "s", &ext_data);
- if (rc == SSH_ERROR) {
- break;
+ rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
}
- SSH_LOG(SSH_LOG_PROTOCOL,
- "SFTP server extension: %s, version: %s",
- ext_name, ext_data);
+ sftp->version = sftp->server_version = (int)version;
- count++;
- tmp = realloc(sftp->ext->name, count * sizeof(char *));
- if (tmp == NULL) {
- ssh_set_error_oom(sftp->session);
- SAFE_FREE(ext_name);
- SAFE_FREE(ext_data);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
- tmp[count - 1] = ext_name;
- sftp->ext->name = tmp;
+ /* Set the limits */
+ rc = sftp_extension_supported(sftp, "limits@openssh.com", "1");
+ if (rc == 1) {
+ /* Get the ssh and sftp errors */
+ const char *static_ssh_err_msg = ssh_get_error(sftp->session);
+ int ssh_err_code = ssh_get_error_code(sftp->session);
+ int sftp_err_code = sftp_get_error(sftp);
+ char *ssh_err_msg = strdup(static_ssh_err_msg);
+ if (ssh_err_msg == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- tmp = realloc(sftp->ext->data, count * sizeof(char *));
- if (tmp == NULL) {
- ssh_set_error_oom(sftp->session);
- SAFE_FREE(ext_name);
- SAFE_FREE(ext_data);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
+ sftp->limits = sftp_limits_use_extension(sftp);
+ if (sftp->limits == NULL) {
+ /* fallback and use the default limits on failure */
+ SSH_LOG(SSH_LOG_TRACE,
+ "Failed to get the limits from a server claiming to "
+ "support the limits@openssh.com extension, falling back "
+ "and using the default limits");
+
+ /* Restore the sftp and ssh errors to their previous state */
+ ssh_set_error(sftp->session, ssh_err_code, "%s", ssh_err_msg);
+ sftp_set_error(sftp, sftp_err_code);
+ SAFE_FREE(ssh_err_msg);
+
+ sftp->limits = sftp_limits_use_default(sftp);
+ if (sftp->limits == NULL) {
+ return -1;
+ }
+ } else {
+ SAFE_FREE(ssh_err_msg);
+ }
+ } else {
+ sftp->limits = sftp_limits_use_default(sftp);
+ if (sftp->limits == NULL) {
+ return -1;
+ }
}
- tmp[count - 1] = ext_data;
- sftp->ext->data = tmp;
-
- sftp->ext->count = count;
-
- rc = ssh_buffer_unpack(packet->payload, "s", &ext_name);
- }
-
- sftp->version = sftp->server_version = (int)version;
-
- return 0;
+ return 0;
}
unsigned int sftp_extensions_get_count(sftp_session sftp) {
@@ -794,166 +628,6 @@ int sftp_extension_supported(sftp_session sftp, const char *name,
return 0;
}
-static sftp_request_queue request_queue_new(sftp_message msg) {
- sftp_request_queue queue = NULL;
-
- queue = calloc(1, sizeof(struct sftp_request_queue_struct));
- if (queue == NULL) {
- ssh_set_error_oom(msg->sftp->session);
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- queue->message = msg;
-
- return queue;
-}
-
-static void request_queue_free(sftp_request_queue queue) {
- if (queue == NULL) {
- return;
- }
-
- ZERO_STRUCTP(queue);
- SAFE_FREE(queue);
-}
-
-static int sftp_enqueue(sftp_session sftp, sftp_message msg) {
- sftp_request_queue queue = NULL;
- sftp_request_queue ptr;
-
- queue = request_queue_new(msg);
- if (queue == NULL) {
- return -1;
- }
-
- SSH_LOG(SSH_LOG_PACKET,
- "Queued msg id %d type %d",
- msg->id, msg->packet_type);
-
- if(sftp->queue == NULL) {
- sftp->queue = queue;
- } else {
- ptr = sftp->queue;
- while(ptr->next) {
- ptr=ptr->next; /* find end of linked list */
- }
- ptr->next = queue; /* add it on bottom */
- }
-
- return 0;
-}
-
-/*
- * Pulls of a message from the queue based on the ID.
- * Returns NULL if no message has been found.
- */
-static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){
- sftp_request_queue prev = NULL;
- sftp_request_queue queue;
- sftp_message msg;
-
- if(sftp->queue == NULL) {
- return NULL;
- }
-
- queue = sftp->queue;
- while (queue) {
- if(queue->message->id == id) {
- /* remove from queue */
- if (prev == NULL) {
- sftp->queue = queue->next;
- } else {
- prev->next = queue->next;
- }
- msg = queue->message;
- request_queue_free(queue);
- SSH_LOG(SSH_LOG_PACKET,
- "Dequeued msg id %d type %d",
- msg->id,
- msg->packet_type);
- return msg;
- }
- prev = queue;
- queue = queue->next;
- }
-
- return NULL;
-}
-
-/*
- * Assigns a new SFTP ID for new requests and assures there is no collision
- * between them.
- * Returns a new ID ready to use in a request
- */
-static inline uint32_t sftp_get_new_id(sftp_session session) {
- return ++session->id_counter;
-}
-
-static sftp_status_message parse_status_msg(sftp_message msg){
- sftp_status_message status;
- int rc;
-
- if (msg->packet_type != SSH_FXP_STATUS) {
- ssh_set_error(msg->sftp->session, SSH_FATAL,
- "Not a ssh_fxp_status message passed in!");
- sftp_set_error(msg->sftp, SSH_FX_BAD_MESSAGE);
- return NULL;
- }
-
- status = calloc(1, sizeof(struct sftp_status_message_struct));
- if (status == NULL) {
- ssh_set_error_oom(msg->sftp->session);
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- status->id = msg->id;
- rc = ssh_buffer_unpack(msg->payload, "d",
- &status->status);
- if (rc != SSH_OK){
- SAFE_FREE(status);
- ssh_set_error(msg->sftp->session, SSH_FATAL,
- "Invalid SSH_FXP_STATUS message");
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
- rc = ssh_buffer_unpack(msg->payload, "ss",
- &status->errormsg,
- &status->langmsg);
-
- if(rc != SSH_OK && msg->sftp->version >=3){
- /* These are mandatory from version 3 */
- SAFE_FREE(status);
- ssh_set_error(msg->sftp->session, SSH_FATAL,
- "Invalid SSH_FXP_STATUS message");
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- return NULL;
- }
- if (status->errormsg == NULL)
- status->errormsg = strdup("No error message in packet");
- if (status->langmsg == NULL)
- status->langmsg = strdup("");
- if (status->errormsg == NULL || status->langmsg == NULL) {
- ssh_set_error_oom(msg->sftp->session);
- sftp_set_error(msg->sftp, SSH_FX_FAILURE);
- status_msg_free(status);
- return NULL;
- }
-
- return status;
-}
-
-static void status_msg_free(sftp_status_message status){
- if (status == NULL) {
- return;
- }
-
- SAFE_FREE(status->errormsg);
- SAFE_FREE(status->langmsg);
- SAFE_FREE(status);
-}
-
static sftp_file parse_handle_msg(sftp_message msg){
sftp_file file;
@@ -993,7 +667,7 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
sftp_file file = NULL;
sftp_dir dir = NULL;
sftp_status_message status;
- ssh_buffer payload;
+ ssh_buffer payload = NULL;
uint32_t id;
int rc;
@@ -1044,7 +718,7 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
}
sftp_set_error(sftp, status->status);
ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
- "SFTP server: %s", status->errormsg);
+ "SFTP server: %s", status->errormsg);
status_msg_free(status);
return NULL;
case SSH_FXP_HANDLE:
@@ -1071,452 +745,14 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
return dir;
default:
ssh_set_error(sftp->session, SSH_FATAL,
- "Received message %d during opendir!", msg->packet_type);
+ "Received message %d during opendir!",
+ msg->packet_type);
sftp_message_free(msg);
}
return NULL;
}
-/*
- * Parse the attributes from a payload from some messages. It is coded on
- * baselines from the protocol version 4.
- * This code is more or less dead but maybe we need it in future.
- */
-static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf,
- int expectnames) {
- sftp_attributes attr;
- ssh_string owner = NULL;
- ssh_string group = NULL;
- uint32_t flags = 0;
- int ok = 0;
-
- /* unused member variable */
- (void) expectnames;
-
- attr = calloc(1, sizeof(struct sftp_attributes_struct));
- if (attr == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- /* This isn't really a loop, but it is like a try..catch.. */
- do {
- if (ssh_buffer_get_u32(buf, &flags) != 4) {
- break;
- }
-
- flags = ntohl(flags);
- attr->flags = flags;
-
- if (flags & SSH_FILEXFER_ATTR_SIZE) {
- if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
- break;
- }
- attr->size = ntohll(attr->size);
- }
-
- if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
- owner = ssh_buffer_get_ssh_string(buf);
- if (owner == NULL) {
- break;
- }
- attr->owner = ssh_string_to_char(owner);
- SSH_STRING_FREE(owner);
- if (attr->owner == NULL) {
- break;
- }
-
- group = ssh_buffer_get_ssh_string(buf);
- if (group == NULL) {
- break;
- }
- attr->group = ssh_string_to_char(group);
- SSH_STRING_FREE(group);
- if (attr->group == NULL) {
- break;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
- break;
- }
- attr->permissions = ntohl(attr->permissions);
-
- /* FIXME on windows! */
- switch (attr->permissions & SSH_S_IFMT) {
- case SSH_S_IFSOCK:
- case SSH_S_IFBLK:
- case SSH_S_IFCHR:
- case SSH_S_IFIFO:
- attr->type = SSH_FILEXFER_TYPE_SPECIAL;
- break;
- case SSH_S_IFLNK:
- attr->type = SSH_FILEXFER_TYPE_SYMLINK;
- break;
- case SSH_S_IFREG:
- attr->type = SSH_FILEXFER_TYPE_REGULAR;
- break;
- case SSH_S_IFDIR:
- attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
- break;
- default:
- attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
- break;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
- if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
- break;
- }
- attr->atime64 = ntohll(attr->atime64);
-
- if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
- if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
- break;
- }
- attr->atime_nseconds = ntohl(attr->atime_nseconds);
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
- if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
- break;
- }
- attr->createtime = ntohll(attr->createtime);
-
- if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
- if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
- break;
- }
- attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
- if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
- break;
- }
- attr->mtime64 = ntohll(attr->mtime64);
-
- if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
- if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
- break;
- }
- attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_ACL) {
- if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
- break;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
- if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
- break;
- }
- attr->extended_count = ntohl(attr->extended_count);
-
- while(attr->extended_count &&
- (attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
- (attr->extended_data = ssh_buffer_get_ssh_string(buf))){
- attr->extended_count--;
- }
-
- if (attr->extended_count) {
- break;
- }
- }
- ok = 1;
- } while (0);
-
- if (ok == 0) {
- /* break issued somewhere */
- SSH_STRING_FREE(attr->acl);
- SSH_STRING_FREE(attr->extended_type);
- SSH_STRING_FREE(attr->extended_data);
- SAFE_FREE(attr->owner);
- SAFE_FREE(attr->group);
- SAFE_FREE(attr);
-
- ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
-
- return NULL;
- }
-
- return attr;
-}
-
-enum sftp_longname_field_e {
- SFTP_LONGNAME_PERM = 0,
- SFTP_LONGNAME_FIXME,
- SFTP_LONGNAME_OWNER,
- SFTP_LONGNAME_GROUP,
- SFTP_LONGNAME_SIZE,
- SFTP_LONGNAME_DATE,
- SFTP_LONGNAME_TIME,
- SFTP_LONGNAME_NAME,
-};
-
-static char *sftp_parse_longname(const char *longname,
- enum sftp_longname_field_e longname_field) {
- const char *p, *q;
- size_t len, field = 0;
-
- p = longname;
- /* Find the beginning of the field which is specified by sftp_longname_field_e. */
- while(field != longname_field) {
- if(isspace(*p)) {
- field++;
- p++;
- while(*p && isspace(*p)) {
- p++;
- }
- } else {
- p++;
- }
- }
-
- q = p;
- while (! isspace(*q)) {
- q++;
- }
-
- len = q - p;
-
- return strndup(p, len);
-}
-
-/* sftp version 0-3 code. It is different from the v4 */
-/* maybe a paste of the draft is better than the code */
-/*
- uint32 flags
- uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
- uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
- uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
- uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
- uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
- uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
- uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
- string extended_type
- string extended_data
- ... more extended data (extended_type - extended_data pairs),
- so that number of pairs equals extended_count */
-static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf,
- int expectname) {
- sftp_attributes attr;
- int rc;
-
- attr = calloc(1, sizeof(struct sftp_attributes_struct));
- if (attr == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- if (expectname) {
- rc = ssh_buffer_unpack(buf, "ss",
- &attr->name,
- &attr->longname);
- if (rc != SSH_OK){
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL, "Name: %s", attr->name);
-
- /* Set owner and group if we talk to openssh and have the longname */
- if (ssh_get_openssh_version(sftp->session)) {
- attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER);
- if (attr->owner == NULL) {
- goto error;
- }
-
- attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP);
- if (attr->group == NULL) {
- goto error;
- }
- }
- }
-
- rc = ssh_buffer_unpack(buf, "d", &attr->flags);
- if (rc != SSH_OK){
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Flags: %.8"PRIx32"\n", (uint32_t) attr->flags);
-
- if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
- rc = ssh_buffer_unpack(buf, "q", &attr->size);
- if(rc != SSH_OK) {
- goto error;
- }
- SSH_LOG(SSH_LOG_PROTOCOL,
- "Size: %"PRIu64"\n",
- (uint64_t) attr->size);
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
- rc = ssh_buffer_unpack(buf, "dd",
- &attr->uid,
- &attr->gid);
- if (rc != SSH_OK){
- goto error;
- }
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
- if (rc != SSH_OK){
- goto error;
- }
-
- switch (attr->permissions & SSH_S_IFMT) {
- case SSH_S_IFSOCK:
- case SSH_S_IFBLK:
- case SSH_S_IFCHR:
- case SSH_S_IFIFO:
- attr->type = SSH_FILEXFER_TYPE_SPECIAL;
- break;
- case SSH_S_IFLNK:
- attr->type = SSH_FILEXFER_TYPE_SYMLINK;
- break;
- case SSH_S_IFREG:
- attr->type = SSH_FILEXFER_TYPE_REGULAR;
- break;
- case SSH_S_IFDIR:
- attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
- break;
- default:
- attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
- break;
- }
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
- rc = ssh_buffer_unpack(buf, "dd",
- &attr->atime,
- &attr->mtime);
- if (rc != SSH_OK){
- goto error;
- }
- }
-
- if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
- rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
- if (rc != SSH_OK){
- goto error;
- }
-
- if (attr->extended_count > 0){
- rc = ssh_buffer_unpack(buf, "ss",
- &attr->extended_type,
- &attr->extended_data);
- if (rc != SSH_OK){
- goto error;
- }
- attr->extended_count--;
- }
- /* just ignore the remaining extensions */
-
- while (attr->extended_count > 0){
- ssh_string tmp1,tmp2;
- rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
- if (rc != SSH_OK){
- goto error;
- }
- SAFE_FREE(tmp1);
- SAFE_FREE(tmp2);
- attr->extended_count--;
- }
- }
-
- return attr;
-
- error:
- SSH_STRING_FREE(attr->extended_type);
- SSH_STRING_FREE(attr->extended_data);
- SAFE_FREE(attr->name);
- SAFE_FREE(attr->longname);
- SAFE_FREE(attr->owner);
- SAFE_FREE(attr->group);
- SAFE_FREE(attr);
- ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
- sftp_set_error(sftp, SSH_FX_FAILURE);
-
- return NULL;
-}
-
-int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr)
-{
- uint32_t flags = (attr ? attr->flags : 0);
- int rc;
-
- flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
- SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
-
- rc = ssh_buffer_pack(buffer, "d", flags);
- if (rc != SSH_OK) {
- return -1;
- }
-
- if (attr != NULL) {
- if (flags & SSH_FILEXFER_ATTR_SIZE) {
- rc = ssh_buffer_pack(buffer, "q", attr->size);
- if (rc != SSH_OK) {
- return -1;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_UIDGID) {
- rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
- if (rc != SSH_OK) {
- return -1;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
- rc = ssh_buffer_pack(buffer, "d", attr->permissions);
- if (rc != SSH_OK) {
- return -1;
- }
- }
-
- if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
- rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
- if (rc != SSH_OK) {
- return -1;
- }
- }
- }
- return 0;
-}
-
-
-sftp_attributes sftp_parse_attr(sftp_session session,
- ssh_buffer buf,
- int expectname)
-{
- switch(session->version) {
- case 4:
- return sftp_parse_attr_4(session, buf, expectname);
- case 3:
- case 2:
- case 1:
- case 0:
- return sftp_parse_attr_3(session, buf, expectname);
- default:
- ssh_set_error(session->session, SSH_FATAL,
- "Version %d unsupported by client", session->server_version);
- return NULL;
- }
-
- return NULL;
-}
-
/* Get the version of the SFTP protocol supported by the server */
int sftp_server_version(sftp_session sftp) {
return sftp->server_version;
@@ -1560,7 +796,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
}
SSH_LOG(SSH_LOG_PACKET,
- "Sent a ssh_fxp_readdir with id %d", id);
+ "Sent a ssh_fxp_readdir with id %" PRIu32, id);
while (msg == NULL) {
if (sftp_read_and_dispatch(sftp) < 0) {
@@ -1588,7 +824,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
}
ssh_set_error(sftp->session, SSH_FATAL,
- "Unknown error status: %d", status->status);
+ "Unknown error status: %" PRIu32, status->status);
status_msg_free(status);
return NULL;
@@ -1617,7 +853,7 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
return NULL;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Count is %d", dir->count);
+ SSH_LOG(SSH_LOG_DEBUG, "Count is %" PRIu32, dir->count);
attr = sftp_parse_attr(sftp, dir->buffer, 1);
if (attr == NULL) {
@@ -1734,6 +970,10 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle)
int sftp_close(sftp_file file){
int err = SSH_NO_ERROR;
+ if (file == NULL) {
+ return err;
+ }
+
SAFE_FREE(file->name);
if (file->handle){
err = sftp_handle_close(file->sftp,file->handle);
@@ -1771,7 +1011,7 @@ sftp_file sftp_open(sftp_session sftp,
sftp_status_message status;
struct sftp_attributes_struct attr;
sftp_file handle;
- ssh_buffer buffer;
+ ssh_buffer buffer = NULL;
sftp_attributes stat_data;
uint32_t sftp_flags = 0;
uint32_t id;
@@ -1803,7 +1043,8 @@ sftp_file sftp_open(sftp_session sftp,
if ((flags & O_APPEND) == O_APPEND) {
sftp_flags |= SSH_FXF_APPEND;
}
- SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags);
+ SSH_LOG(SSH_LOG_PACKET, "Opening file %s with sftp flags %" PRIx32,
+ file, sftp_flags);
id = sftp_get_new_id(sftp);
rc = ssh_buffer_pack(buffer,
@@ -1841,62 +1082,66 @@ sftp_file sftp_open(sftp_session sftp,
}
switch (msg->packet_type) {
- case SSH_FXP_STATUS:
- status = parse_status_msg(msg);
- sftp_message_free(msg);
- if (status == NULL) {
- return NULL;
- }
- sftp_set_error(sftp, status->status);
- ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
- "SFTP server: %s", status->errormsg);
- status_msg_free(status);
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ handle = parse_handle_msg(msg);
+ if (handle == NULL) {
return NULL;
- case SSH_FXP_HANDLE:
- handle = parse_handle_msg(msg);
- if (handle == NULL) {
+ }
+ sftp_message_free(msg);
+ if ((flags & O_APPEND) == O_APPEND) {
+ stat_data = sftp_stat(sftp, file);
+ if (stat_data == NULL) {
+ sftp_close(handle);
return NULL;
}
- sftp_message_free(msg);
- if ((flags & O_APPEND) == O_APPEND) {
- stat_data = sftp_stat(sftp, file);
- if (stat_data == NULL) {
- sftp_close(handle);
- return NULL;
- }
- if ((stat_data->flags & SSH_FILEXFER_ATTR_SIZE) != SSH_FILEXFER_ATTR_SIZE) {
- ssh_set_error(sftp->session,
- SSH_FATAL,
- "Cannot open in append mode. Unknown file size.");
- sftp_close(handle);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return NULL;
- }
-
- handle->offset = stat_data->size;
+ if ((stat_data->flags & SSH_FILEXFER_ATTR_SIZE) != SSH_FILEXFER_ATTR_SIZE) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Cannot open in append mode. Unknown file size.");
+ sftp_attributes_free(stat_data);
+ sftp_close(handle);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
}
- return handle;
- default:
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received message %d during open!", msg->packet_type);
- sftp_message_free(msg);
- sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+
+ handle->offset = stat_data->size;
+ sftp_attributes_free(stat_data);
+ }
+ return handle;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during open!", msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
}
return NULL;
}
-void sftp_file_set_nonblocking(sftp_file handle){
- handle->nonblocking=1;
+void sftp_file_set_nonblocking(sftp_file handle)
+{
+ handle->nonblocking = 1;
}
-void sftp_file_set_blocking(sftp_file handle){
- handle->nonblocking=0;
+void sftp_file_set_blocking(sftp_file handle)
+{
+ handle->nonblocking = 0;
}
/* Read from a file using an opened sftp file handle. */
ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
- sftp_session sftp = handle->sftp;
+ sftp_session sftp;
sftp_message msg = NULL;
sftp_status_message status;
ssh_string datastring;
@@ -1905,10 +1150,27 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
uint32_t id;
int rc;
+ if (handle == NULL) {
+ return -1;
+ }
+ sftp = handle->sftp;
+
if (handle->eof) {
return 0;
}
+ /*
+ * limit the reads to the maximum specified in Section 3 of
+ * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
+ * or to the values provided by the limits@openssh.com extension.
+ *
+ * TODO: We should iterate over the blocks rather than writing less than
+ * requested to provide less surprises to the calling applications.
+ */
+ if (count > sftp->limits->max_read_length) {
+ count = sftp->limits->max_read_length;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2104,7 +1366,7 @@ int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){
if (ssh_string_len(datastring) > size) {
ssh_set_error(sftp->session, SSH_FATAL,
"Received a too big DATA packet from sftp server: "
- "%zu and asked for %u",
+ "%zu and asked for %" PRIu32,
ssh_string_len(datastring), size);
SSH_STRING_FREE(datastring);
return SSH_ERROR;
@@ -2126,7 +1388,7 @@ int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){
}
ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
- sftp_session sftp = file->sftp;
+ sftp_session sftp;
sftp_message msg = NULL;
sftp_status_message status;
ssh_buffer buffer;
@@ -2135,6 +1397,11 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
size_t packetlen;
int rc;
+ if (file == NULL) {
+ return -1;
+ }
+ sftp = file->sftp;
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2144,6 +1411,18 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
id = sftp_get_new_id(file->sftp);
+ /*
+ * limit the writes to the maximum specified in Section 3 of
+ * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
+ * or to the values provided by the limits@openssh.com extension.
+ *
+ * TODO: We should iterate over the blocks rather than writing less than
+ * requested to provide less surprises to the calling applications.
+ */
+ if (count > sftp->limits->max_write_length) {
+ count = sftp->limits->max_write_length;
+ }
+
rc = ssh_buffer_pack(buffer,
"dSqdP",
id,
@@ -2157,8 +1436,8 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
sftp_set_error(sftp, SSH_FX_FAILURE);
return -1;
}
- packetlen=ssh_buffer_get_len(buffer);
len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer);
+ packetlen=ssh_buffer_get_len(buffer);
SSH_BUFFER_FREE(buffer);
if (len < 0) {
return -1;
@@ -2493,85 +1772,115 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode)
}
/* code written by nick */
-int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
- sftp_status_message status = NULL;
- sftp_message msg = NULL;
- ssh_buffer buffer;
- uint32_t id;
- int rc;
+int sftp_rename(sftp_session sftp, const char *original, const char *newname)
+{
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ const char *extension_name = "posix-rename@openssh.com";
+ int request_type;
+ int rc;
- buffer = ssh_buffer_new();
- if (buffer == NULL) {
- ssh_set_error_oom(sftp->session);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- id = sftp_get_new_id(sftp);
+ id = sftp_get_new_id(sftp);
- rc = ssh_buffer_pack(buffer,
- "dss",
- id,
- original,
- newname);
- if (rc != SSH_OK) {
- ssh_set_error_oom(sftp->session);
- SSH_BUFFER_FREE(buffer);
- sftp_set_error(sftp, SSH_FX_FAILURE);
- return -1;
- }
+ /*
+ * posix-rename@openssh.com extension will be used
+ * if it is supported by sftp
+ */
+ if (sftp_extension_supported(sftp,
+ extension_name,
+ "1")) {
+ rc = ssh_buffer_pack(buffer,
+ "dsss",
+ id,
+ extension_name,
+ original,
+ newname);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- if (sftp->version >= 4){
- /* POSIX rename atomically replaces newpath, we should do the same
- * only available on >=v4 */
- ssh_buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE);
- }
+ request_type = SSH_FXP_EXTENDED;
+ } else {
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ original,
+ newname);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
- if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) {
- SSH_BUFFER_FREE(buffer);
- return -1;
- }
- SSH_BUFFER_FREE(buffer);
+ if (sftp->version >= 4) {
+ /*
+ * POSIX rename atomically replaces newpath,
+ * we should do the same only available on >=v4
+ */
+ ssh_buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE);
+ }
- while (msg == NULL) {
- if (sftp_read_and_dispatch(sftp) < 0) {
- return -1;
+ request_type = SSH_FXP_RENAME;
}
- msg = sftp_dequeue(sftp, id);
- }
- /* By specification, this command only returns SSH_FXP_STATUS */
- if (msg->packet_type == SSH_FXP_STATUS) {
- status = parse_status_msg(msg);
- sftp_message_free(msg);
- if (status == NULL) {
- return -1;
+ rc = sftp_packet_write(sftp, request_type, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc < 0) {
+ return -1;
}
- sftp_set_error(sftp, status->status);
- switch (status->status) {
- case SSH_FX_OK:
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * Status should be SSH_FX_OK if the command was successful,
+ * if it didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
status_msg_free(status);
- return 0;
- default:
- break;
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to rename",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
}
- /*
- * Status should be SSH_FX_OK if the command was successful, if it didn't,
- * then there was an error
- */
- ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
- "SFTP server: %s", status->errormsg);
- status_msg_free(status);
- return -1;
- } else {
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received message %d when attempting to rename",
- msg->packet_type);
- sftp_message_free(msg);
- sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
- }
- return -1;
+ return -1;
}
/* Code written by Nick */
@@ -2699,7 +2008,8 @@ int sftp_utimes(sftp_session sftp, const char *file,
return sftp_setstat(sftp, file, &attr);
}
-int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
+int sftp_symlink(sftp_session sftp, const char *target, const char *dest)
+{
sftp_status_message status = NULL;
sftp_message msg = NULL;
ssh_buffer buffer;
@@ -2723,7 +2033,10 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
id = sftp_get_new_id(sftp);
- /* TODO check for version number if they ever fix it. */
+ /* The OpenSSH sftp server has order of the arguments reversed, see the
+ * section "4.1 sftp: Reversal of arguments to SSH_FXP_SYMLINK' in
+ * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
+ * for more information */
if (ssh_get_openssh_version(sftp->session)) {
rc = ssh_buffer_pack(buffer,
"dss",
@@ -2883,6 +2196,94 @@ char *sftp_readlink(sftp_session sftp, const char *path)
return NULL;
}
+int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath)
+{
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ const char *extension_name = "hardlink@openssh.com";
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ int rc;
+
+ if (sftp == NULL) {
+ return -1;
+ }
+
+ if (oldpath == NULL || newpath == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dsss",
+ id,
+ extension_name,
+ oldpath,
+ newpath);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc < 0) {
+ return -1;
+ }
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ /* By specification, this command only returns SSH_FXP_STATUS */
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return -1;
+ }
+ sftp_set_error(sftp, status->status);
+ switch (status->status) {
+ case SSH_FX_OK:
+ status_msg_free(status);
+ return 0;
+ default:
+ break;
+ }
+ /*
+ * Status should be SSH_FX_OK if the command was successful,
+ * if it didn't, then there was an error
+ */
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to create hardlink",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ }
+
+ return -1;
+}
+
static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) {
sftp_statvfs_t statvfs;
int rc;
@@ -3183,6 +2584,183 @@ void sftp_statvfs_free(sftp_statvfs_t statvfs) {
SAFE_FREE(statvfs);
}
+static sftp_limits_t sftp_limits_new(void)
+{
+ return calloc(1, sizeof(struct sftp_limits_struct));
+}
+
+static sftp_limits_t sftp_parse_limits(sftp_session sftp, ssh_buffer buf)
+{
+ sftp_limits_t limits = NULL;
+ int rc;
+
+ limits = sftp_limits_new();
+ if (limits == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ rc = ssh_buffer_unpack(buf, "qqqq",
+ &limits->max_packet_length, /** maximum number of bytes in a single sftp packet */
+ &limits->max_read_length, /** maximum length in a SSH_FXP_READ packet */
+ &limits->max_write_length, /** maximum length in a SSH_FXP_WRITE packet */
+ &limits->max_open_handles /** maximum number of active handles allowed by server */
+ );
+ if (rc != SSH_OK) {
+ SAFE_FREE(limits);
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid limits structure");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ return limits;
+}
+
+static sftp_limits_t sftp_limits_use_extension(sftp_session sftp)
+{
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL)
+ return NULL;
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "ds",
+ id,
+ "limits@openssh.com");
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc < 0) {
+ return NULL;
+ }
+
+ while (msg == NULL) {
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
+ sftp_limits_t limits = sftp_parse_limits(sftp, msg->payload);
+ sftp_message_free(msg);
+ if (limits == NULL) {
+ return NULL;
+ }
+
+ return limits;
+ } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session,
+ SSH_REQUEST_DENIED,
+ "SFTP server: %s",
+ status->errormsg);
+ status_msg_free(status);
+ } else { /* this shouldn't happen */
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received message %d when attempting to get limits",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ }
+
+ return NULL;
+}
+
+static sftp_limits_t sftp_limits_use_default(sftp_session sftp)
+{
+ sftp_limits_t limits = NULL;
+
+ if (sftp == NULL) {
+ return NULL;
+ }
+
+ limits = sftp_limits_new();
+ if (limits == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ limits->max_packet_length = 34000;
+ limits->max_read_length = 32768;
+ limits->max_write_length = 32768;
+
+ /*
+ * For max-open-handles field openssh says :
+ * If the server doesn't enforce a specific limit, then the field may
+ * be set to 0. This implies the server relies on the OS to enforce
+ * limits (e.g. available memory or file handles), and such limits
+ * might be dynamic. The client SHOULD take care to not try to exceed
+ * reasonable limits.
+ */
+ limits->max_open_handles = 0;
+
+ return limits;
+}
+
+sftp_limits_t sftp_limits(sftp_session sftp)
+{
+ sftp_limits_t limits = NULL;
+
+ if (sftp == NULL) {
+ return NULL;
+ }
+
+ if (sftp->limits == NULL) {
+ ssh_set_error(sftp, SSH_FATAL,
+ "Uninitialized sftp session, "
+ "sftp_init() was not called or failed");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ limits = sftp_limits_new();
+ if (limits == NULL) {
+ ssh_set_error_oom(sftp);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ memcpy(limits, sftp->limits, sizeof(struct sftp_limits_struct));
+ return limits;
+}
+
+void sftp_limits_free(sftp_limits_t limits)
+{
+ if (limits == NULL) {
+ return;
+ }
+
+ SAFE_FREE(limits);
+}
+
/* another code written by Nick */
char *sftp_canonicalize_path(sftp_session sftp, const char *path)
{
@@ -3428,4 +3006,98 @@ sftp_attributes sftp_fstat(sftp_file file)
return NULL;
}
+char *sftp_expand_path(sftp_session sftp, const char *path)
+{
+ sftp_status_message status = NULL;
+ sftp_message msg = NULL;
+ ssh_buffer buffer = NULL;
+ uint32_t id;
+ int rc;
+
+ if (sftp == NULL) {
+ return NULL;
+ }
+
+ if (path == NULL) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "NULL received as an argument instead of the path to expand");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dss",
+ id,
+ "expand-path@openssh.com",
+ path);
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ SSH_BUFFER_FREE(buffer);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc < 0) {
+ return NULL;
+ }
+
+ while (msg == NULL) {
+ rc = sftp_read_and_dispatch(sftp);
+ if (rc < 0) {
+ return NULL;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+
+ if (msg->packet_type == SSH_FXP_NAME) {
+ uint32_t ignored = 0;
+ char *cname = NULL;
+
+ rc = ssh_buffer_unpack(msg->payload,
+ "ds",
+ &ignored,
+ &cname);
+ sftp_message_free(msg);
+ if (rc != SSH_OK) {
+ ssh_set_error(sftp->session,
+ SSH_ERROR,
+ "Failed to parse expanded path");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ return cname;
+ } else if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return NULL;
+ }
+ sftp_set_error(sftp, status->status);
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ } else {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d when attempting to expand path",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ }
+
+ return NULL;
+}
+
#endif /* WITH_SFTP */
diff --git a/src/sftp_aio.c b/src/sftp_aio.c
new file mode 100644
index 00000000..c1c54561
--- /dev/null
+++ b/src/sftp_aio.c
@@ -0,0 +1,500 @@
+/*
+ * sftp_aio.c - Secure FTP functions for asynchronous i/o
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2005-2008 by Aris Adamantiadis
+ * Copyright (c) 2008-2018 by Andreas Schneider <asn@cryptomilk.org>
+ * Copyright (c) 2023 by Eshan Kelkar <eshankelkar@galorithm.com>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "libssh/sftp.h"
+#include "libssh/sftp_priv.h"
+#include "libssh/buffer.h"
+#include "libssh/session.h"
+
+#ifdef WITH_SFTP
+
+struct sftp_aio_struct {
+ sftp_file file;
+ uint32_t id;
+ size_t len;
+};
+
+static sftp_aio sftp_aio_new(void)
+{
+ sftp_aio aio = NULL;
+ aio = calloc(1, sizeof(struct sftp_aio_struct));
+ return aio;
+}
+
+void sftp_aio_free(sftp_aio aio)
+{
+ SAFE_FREE(aio);
+}
+
+ssize_t sftp_aio_begin_read(sftp_file file, size_t len, sftp_aio *aio)
+{
+ sftp_session sftp = NULL;
+ ssh_buffer buffer = NULL;
+ sftp_aio aio_handle = NULL;
+ uint32_t id;
+ int rc;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (len == 0) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, 0 passed as the number of "
+ "bytes to read");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ /* Apply a cap on the length a user is allowed to read */
+ if (len > sftp->limits->max_read_length) {
+ len = sftp->limits->max_read_length;
+ }
+
+ if (aio == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed instead of a pointer to "
+ "a location to store an sftp aio handle");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ id = sftp_get_new_id(sftp);
+
+ rc = ssh_buffer_pack(buffer,
+ "dSqd",
+ id,
+ file->handle,
+ file->offset,
+ len);
+
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle = sftp_aio_new();
+ if (aio_handle == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle->file = file;
+ aio_handle->id = id;
+ aio_handle->len = len;
+
+ rc = sftp_packet_write(sftp, SSH_FXP_READ, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc == SSH_ERROR) {
+ SFTP_AIO_FREE(aio_handle);
+ return SSH_ERROR;
+ }
+
+ /* Assume we read len bytes from the file */
+ file->offset += len;
+ *aio = aio_handle;
+ return len;
+}
+
+ssize_t sftp_aio_wait_read(sftp_aio *aio,
+ void *buf,
+ size_t buf_size)
+{
+ sftp_file file = NULL;
+ size_t bytes_requested;
+ sftp_session sftp = NULL;
+ sftp_message msg = NULL;
+ sftp_status_message status = NULL;
+ uint32_t string_len, host_len;
+ int rc, err;
+
+ /*
+ * This function releases the memory of the structure
+ * that (*aio) points to in all cases except when the
+ * return value is SSH_AGAIN.
+ *
+ * If the return value is SSH_AGAIN, the user should call this
+ * function again to get the response for the request corresponding
+ * to the structure that (*aio) points to, hence we don't release the
+ * structure's memory when SSH_AGAIN is returned.
+ */
+
+ if (aio == NULL || *aio == NULL) {
+ return SSH_ERROR;
+ }
+
+ file = (*aio)->file;
+ bytes_requested = (*aio)->len;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (bytes_requested == 0) {
+ /* should never happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid sftp aio, len for requested i/o is 0");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ if (buf == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed "
+ "instead of a buffer's address");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ if (buf_size < bytes_requested) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Buffer size (%zu, passed by the caller) is "
+ "smaller than the number of bytes requested "
+ "to read (%zu, as per the supplied sftp aio)",
+ buf_size, bytes_requested);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ /* handle an existing request */
+ while (msg == NULL) {
+ if (file->nonblocking) {
+ if (ssh_channel_poll(sftp->channel, 0) == 0) {
+ /* we cannot block */
+ return SSH_AGAIN;
+ }
+ }
+
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ msg = sftp_dequeue(sftp, (*aio)->id);
+ }
+
+ /*
+ * Release memory for the structure that (*aio) points to
+ * as all further points of return are for success or
+ * failure.
+ */
+ SFTP_AIO_FREE(*aio);
+
+ switch (msg->packet_type) {
+ case SSH_FXP_STATUS:
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp_set_error(sftp, status->status);
+ if (status->status != SSH_FX_EOF) {
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server : %s", status->errormsg);
+ err = SSH_ERROR;
+ } else {
+ file->eof = 1;
+ /* Update the offset correctly */
+ file->offset = file->offset - bytes_requested;
+ err = SSH_OK;
+ }
+
+ status_msg_free(status);
+ return err;
+
+ case SSH_FXP_DATA:
+ rc = ssh_buffer_get_u32(msg->payload, &string_len);
+ if (rc == 0) {
+ /* Insufficient data in the buffer */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received invalid DATA packet from sftp server");
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ host_len = ntohl(string_len);
+ if (host_len > buf_size) {
+ /*
+ * This should never happen, as according to the
+ * SFTP protocol the server reads bytes less than
+ * or equal to the number of bytes requested to read.
+ *
+ * And we have checked before that the buffer size is
+ * greater than or equal to the number of bytes requested
+ * to read, hence code of this if block should never
+ * get executed.
+ */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "DATA packet (%u bytes) received from sftp server "
+ "cannot fit into the supplied buffer (%zu bytes)",
+ host_len, buf_size);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ string_len = ssh_buffer_get_data(msg->payload, buf, host_len);
+ if (string_len != host_len) {
+ /* should never happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received invalid DATA packet from sftp server");
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ /* Update the offset with the correct value */
+ file->offset = file->offset - (bytes_requested - string_len);
+ sftp_message_free(msg);
+ return string_len;
+
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during read!", msg->packet_type);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ sftp_message_free(msg);
+ return SSH_ERROR;
+ }
+
+ return SSH_ERROR; /* not reached */
+}
+
+ssize_t sftp_aio_begin_write(sftp_file file,
+ const void *buf,
+ size_t len,
+ sftp_aio *aio)
+{
+ sftp_session sftp = NULL;
+ ssh_buffer buffer = NULL;
+ sftp_aio aio_handle = NULL;
+ uint32_t id;
+ int rc;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (buf == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed instead "
+ "of a buffer's address");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ if (len == 0) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, 0 passed as the number "
+ "of bytes to write");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ /* Apply a cap on the length a user is allowed to write */
+ if (len > sftp->limits->max_write_length) {
+ len = sftp->limits->max_write_length;
+ }
+
+ if (aio == NULL) {
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid argument, NULL passed instead of a pointer to "
+ "a location to store an sftp aio handle");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ id = sftp_get_new_id(sftp);
+ rc = ssh_buffer_pack(buffer,
+ "dSqdP",
+ id,
+ file->handle,
+ file->offset,
+ len, /* len of datastring */
+ len, buf);
+
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle = sftp_aio_new();
+ if (aio_handle == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SSH_BUFFER_FREE(buffer);
+ return SSH_ERROR;
+ }
+
+ aio_handle->file = file;
+ aio_handle->id = id;
+ aio_handle->len = len;
+
+ rc = sftp_packet_write(sftp, SSH_FXP_WRITE, buffer);
+ SSH_BUFFER_FREE(buffer);
+ if (rc == SSH_ERROR) {
+ SFTP_AIO_FREE(aio_handle);
+ return SSH_ERROR;
+ }
+
+ /* Assume we wrote len bytes to the file */
+ file->offset += len;
+ *aio = aio_handle;
+ return len;
+}
+
+ssize_t sftp_aio_wait_write(sftp_aio *aio)
+{
+ sftp_file file = NULL;
+ size_t bytes_requested;
+
+ sftp_session sftp = NULL;
+ sftp_message msg = NULL;
+ sftp_status_message status = NULL;
+
+ /*
+ * This function releases the memory of the structure
+ * that (*aio) points to in all cases except when the
+ * return value is SSH_AGAIN.
+ *
+ * If the return value is SSH_AGAIN, the user should call this
+ * function again to get the response for the request corresponding
+ * to the structure that (*aio) points to, hence we don't release the
+ * structure's memory when SSH_AGAIN is returned.
+ */
+
+ if (aio == NULL || *aio == NULL) {
+ return SSH_ERROR;
+ }
+
+ file = (*aio)->file;
+ bytes_requested = (*aio)->len;
+
+ if (file == NULL ||
+ file->sftp == NULL ||
+ file->sftp->session == NULL) {
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ sftp = file->sftp;
+ if (bytes_requested == 0) {
+ /* This should never happen */
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Invalid sftp aio, len for requested i/o is 0");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ while (msg == NULL) {
+ if (file->nonblocking) {
+ if (ssh_channel_poll(sftp->channel, 0) == 0) {
+ /* we cannot block */
+ return SSH_AGAIN;
+ }
+ }
+
+ if (sftp_read_and_dispatch(sftp) < 0) {
+ /* something nasty has happened */
+ SFTP_AIO_FREE(*aio);
+ return SSH_ERROR;
+ }
+
+ msg = sftp_dequeue(sftp, (*aio)->id);
+ }
+
+ /*
+ * Release memory for the structure that (*aio) points to
+ * as all further points of return are for success or
+ * failure.
+ */
+ SFTP_AIO_FREE(*aio);
+
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (status == NULL) {
+ return SSH_ERROR;
+ }
+
+ sftp_set_error(sftp, status->status);
+ if (status->status == SSH_FX_OK) {
+ status_msg_free(status);
+ return bytes_requested;
+ }
+
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
+ "SFTP server: %s", status->errormsg);
+ status_msg_free(status);
+ return SSH_ERROR;
+ }
+
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received message %d during write!",
+ msg->packet_type);
+ sftp_message_free(msg);
+ sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
+ return SSH_ERROR;
+}
+
+#endif /* WITH_SFTP */
diff --git a/src/sftp_common.c b/src/sftp_common.c
new file mode 100644
index 00000000..005fa9da
--- /dev/null
+++ b/src/sftp_common.c
@@ -0,0 +1,913 @@
+/*
+ * sftp_common.c - Secure FTP functions which are private and are used
+ * internally by other sftp api functions spread across
+ * various source files.
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2005-2008 by Aris Adamantiadis
+ * Copyright (c) 2008-2018 by Andreas Schneider <asn@cryptomilk.org>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+
+#include "libssh/sftp.h"
+#include "libssh/sftp_priv.h"
+#include "libssh/buffer.h"
+#include "libssh/session.h"
+#include "libssh/bytearray.h"
+
+#ifdef WITH_SFTP
+
+/* Buffer size maximum is 256M */
+#define SFTP_PACKET_SIZE_MAX 0x10000000
+
+sftp_packet sftp_packet_read(sftp_session sftp)
+{
+ uint8_t tmpbuf[4];
+ uint8_t *buffer = NULL;
+ sftp_packet packet = sftp->read_packet;
+ uint32_t size;
+ int nread;
+ bool is_eof;
+ int rc;
+
+ packet->sftp = sftp;
+
+ /*
+ * If the packet has a payload, then just reinit the buffer, otherwise
+ * allocate a new one.
+ */
+ if (packet->payload != NULL) {
+ rc = ssh_buffer_reinit(packet->payload);
+ if (rc != 0) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+ } else {
+ packet->payload = ssh_buffer_new();
+ if (packet->payload == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+ }
+
+ nread = 0;
+ do {
+ int s;
+
+ /* read from channel until 4 bytes have been read or an error occurs */
+ s = ssh_channel_read(sftp->channel, tmpbuf + nread, 4 - nread, 0);
+ if (s < 0) {
+ goto error;
+ } else if (s == 0) {
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received EOF while reading sftp packet size");
+ sftp_set_error(sftp, SSH_FX_EOF);
+ goto error;
+ }
+ } else {
+ nread += s;
+ }
+ } while (nread < 4);
+
+ size = PULL_BE_U32(tmpbuf, 0);
+ if (size == 0 || size > SFTP_PACKET_SIZE_MAX) {
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid sftp packet size!");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ goto error;
+ }
+
+ do {
+ nread = ssh_channel_read(sftp->channel, tmpbuf, 1, 0);
+ if (nread < 0) {
+ goto error;
+ } else if (nread == 0) {
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Received EOF while reading sftp packet type");
+ sftp_set_error(sftp, SSH_FX_EOF);
+ goto error;
+ }
+ }
+ } while (nread < 1);
+
+ packet->type = tmpbuf[0];
+
+ /* Remove the packet type size */
+ size -= sizeof(uint8_t);
+
+ /* Allocate the receive buffer from payload */
+ buffer = ssh_buffer_allocate(packet->payload, size);
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ goto error;
+ }
+ while (size > 0 && size < SFTP_PACKET_SIZE_MAX) {
+ nread = ssh_channel_read(sftp->channel, buffer, size, 0);
+ if (nread < 0) {
+ /* TODO: check if there are cases where an error needs to be set here */
+ goto error;
+ }
+
+ if (nread > 0) {
+ buffer += nread;
+ size -= nread;
+ } else { /* nread == 0 */
+ /* Retry the reading unless the remote was closed */
+ is_eof = ssh_channel_is_eof(sftp->channel);
+ if (is_eof) {
+ ssh_set_error(sftp->session,
+ SSH_REQUEST_DENIED,
+ "Received EOF while reading sftp packet");
+ sftp_set_error(sftp, SSH_FX_EOF);
+ goto error;
+ }
+ }
+ }
+
+ return packet;
+error:
+ ssh_buffer_reinit(packet->payload);
+ return NULL;
+}
+
+int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
+{
+ uint8_t header[5] = {0};
+ uint32_t payload_size;
+ int size;
+ int rc;
+
+ /* Add size of type */
+ payload_size = ssh_buffer_get_len(payload) + sizeof(uint8_t);
+ PUSH_BE_U32(header, 0, payload_size);
+ PUSH_BE_U8(header, 4, type);
+
+ rc = ssh_buffer_prepend_data(payload, header, sizeof(header));
+ if (rc < 0) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ size = ssh_channel_write(sftp->channel,
+ ssh_buffer_get(payload),
+ ssh_buffer_get_len(payload));
+ if (size < 0) {
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return -1;
+ }
+
+ if ((uint32_t)size != ssh_buffer_get_len(payload)) {
+ SSH_LOG(SSH_LOG_PACKET,
+ "Had to write %" PRIu32 " bytes, wrote only %d",
+ ssh_buffer_get_len(payload),
+ size);
+ }
+
+ return size;
+}
+
+void sftp_packet_free(sftp_packet packet)
+{
+ if (packet == NULL) {
+ return;
+ }
+
+ SSH_BUFFER_FREE(packet->payload);
+ free(packet);
+}
+
+int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr)
+{
+ uint32_t flags = (attr ? attr->flags : 0);
+ int rc;
+
+ flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
+ SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+
+ rc = ssh_buffer_pack(buffer, "d", flags);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
+ if (attr != NULL) {
+ if (flags & SSH_FILEXFER_ATTR_SIZE) {
+ rc = ssh_buffer_pack(buffer, "q", attr->size);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_UIDGID) {
+ rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ rc = ssh_buffer_pack(buffer, "d", attr->permissions);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Parse the attributes from a payload from some messages. It is coded on
+ * baselines from the protocol version 4.
+ * This code is more or less dead but maybe we will need it in the future.
+ */
+static sftp_attributes sftp_parse_attr_4(sftp_session sftp,
+ ssh_buffer buf,
+ int expectnames)
+{
+ sftp_attributes attr = NULL;
+ ssh_string owner = NULL;
+ ssh_string group = NULL;
+ uint32_t flags = 0;
+ int ok = 0;
+
+ /* unused member variable */
+ (void) expectnames;
+
+ attr = calloc(1, sizeof(struct sftp_attributes_struct));
+ if (attr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ /* This isn't really a loop, but it is like a try..catch.. */
+ do {
+ if (ssh_buffer_get_u32(buf, &flags) != 4) {
+ break;
+ }
+
+ flags = ntohl(flags);
+ attr->flags = flags;
+
+ if (flags & SSH_FILEXFER_ATTR_SIZE) {
+ if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
+ break;
+ }
+ attr->size = ntohll(attr->size);
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
+ owner = ssh_buffer_get_ssh_string(buf);
+ if (owner == NULL) {
+ break;
+ }
+ attr->owner = ssh_string_to_char(owner);
+ SSH_STRING_FREE(owner);
+ if (attr->owner == NULL) {
+ break;
+ }
+
+ group = ssh_buffer_get_ssh_string(buf);
+ if (group == NULL) {
+ break;
+ }
+ attr->group = ssh_string_to_char(group);
+ SSH_STRING_FREE(group);
+ if (attr->group == NULL) {
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
+ break;
+ }
+ attr->permissions = ntohl(attr->permissions);
+
+ /* FIXME on windows! */
+ switch (attr->permissions & SSH_S_IFMT) {
+ case SSH_S_IFSOCK:
+ case SSH_S_IFBLK:
+ case SSH_S_IFCHR:
+ case SSH_S_IFIFO:
+ attr->type = SSH_FILEXFER_TYPE_SPECIAL;
+ break;
+ case SSH_S_IFLNK:
+ attr->type = SSH_FILEXFER_TYPE_SYMLINK;
+ break;
+ case SSH_S_IFREG:
+ attr->type = SSH_FILEXFER_TYPE_REGULAR;
+ break;
+ case SSH_S_IFDIR:
+ attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
+ break;
+ default:
+ attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
+ if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
+ break;
+ }
+ attr->atime64 = ntohll(attr->atime64);
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
+ break;
+ }
+ attr->atime_nseconds = ntohl(attr->atime_nseconds);
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
+ if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
+ break;
+ }
+ attr->createtime = ntohll(attr->createtime);
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
+ break;
+ }
+ attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
+ if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
+ break;
+ }
+ attr->mtime64 = ntohll(attr->mtime64);
+
+ if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
+ if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
+ break;
+ }
+ attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_ACL) {
+ if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
+ break;
+ }
+ }
+
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
+ break;
+ }
+ attr->extended_count = ntohl(attr->extended_count);
+
+ while (attr->extended_count &&
+ (attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
+ (attr->extended_data = ssh_buffer_get_ssh_string(buf))) {
+ attr->extended_count--;
+ }
+
+ if (attr->extended_count) {
+ break;
+ }
+ }
+ ok = 1;
+ } while (0);
+
+ if (ok == 0) {
+ /* break issued somewhere */
+ SSH_STRING_FREE(attr->acl);
+ SSH_STRING_FREE(attr->extended_type);
+ SSH_STRING_FREE(attr->extended_data);
+ SAFE_FREE(attr->owner);
+ SAFE_FREE(attr->group);
+ SAFE_FREE(attr);
+
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
+
+ return NULL;
+ }
+
+ return attr;
+}
+
+enum sftp_longname_field_e {
+ SFTP_LONGNAME_PERM = 0,
+ SFTP_LONGNAME_FIXME,
+ SFTP_LONGNAME_OWNER,
+ SFTP_LONGNAME_GROUP,
+ SFTP_LONGNAME_SIZE,
+ SFTP_LONGNAME_DATE,
+ SFTP_LONGNAME_TIME,
+ SFTP_LONGNAME_NAME,
+};
+
+static char * sftp_parse_longname(const char *longname,
+ enum sftp_longname_field_e longname_field)
+{
+ const char *p, *q;
+ size_t len, field = 0;
+
+ p = longname;
+ /*
+ * Find the beginning of the field which is specified
+ * by sftp_longname_field_e.
+ */
+ while (field != longname_field) {
+ if (isspace(*p)) {
+ field++;
+ p++;
+ while (*p && isspace(*p)) {
+ p++;
+ }
+ } else {
+ p++;
+ }
+ }
+
+ q = p;
+ while (! isspace(*q)) {
+ q++;
+ }
+
+ len = q - p;
+
+ return strndup(p, len);
+}
+
+/* sftp version 0-3 code. It is different from the v4 */
+/* maybe a paste of the draft is better than the code */
+/*
+ uint32 flags
+ uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ string extended_type
+ string extended_data
+ ... more extended data (extended_type - extended_data pairs),
+ so that number of pairs equals extended_count */
+static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
+ ssh_buffer buf,
+ int expectname)
+{
+ sftp_attributes attr;
+ int rc;
+
+ attr = calloc(1, sizeof(struct sftp_attributes_struct));
+ if (attr == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ if (expectname) {
+ rc = ssh_buffer_unpack(buf, "ss",
+ &attr->name,
+ &attr->longname);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG, "Name: %s", attr->name);
+
+ /* Set owner and group if we talk to openssh and have the longname */
+ if (ssh_get_openssh_version(sftp->session)) {
+ attr->owner = sftp_parse_longname(attr->longname,
+ SFTP_LONGNAME_OWNER);
+ if (attr->owner == NULL) {
+ goto error;
+ }
+
+ attr->group = sftp_parse_longname(attr->longname,
+ SFTP_LONGNAME_GROUP);
+ if (attr->group == NULL) {
+ goto error;
+ }
+ }
+ }
+
+ rc = ssh_buffer_unpack(buf, "d", &attr->flags);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Flags: %.8" PRIx32 "\n", attr->flags);
+
+ if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
+ rc = ssh_buffer_unpack(buf, "q", &attr->size);
+ if(rc != SSH_OK) {
+ goto error;
+ }
+ SSH_LOG(SSH_LOG_DEBUG,
+ "Size: %" PRIu64 "\n",
+ (uint64_t) attr->size);
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
+ rc = ssh_buffer_unpack(buf, "dd",
+ &attr->uid,
+ &attr->gid);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
+ rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ switch (attr->permissions & SSH_S_IFMT) {
+ case SSH_S_IFSOCK:
+ case SSH_S_IFBLK:
+ case SSH_S_IFCHR:
+ case SSH_S_IFIFO:
+ attr->type = SSH_FILEXFER_TYPE_SPECIAL;
+ break;
+ case SSH_S_IFLNK:
+ attr->type = SSH_FILEXFER_TYPE_SYMLINK;
+ break;
+ case SSH_S_IFREG:
+ attr->type = SSH_FILEXFER_TYPE_REGULAR;
+ break;
+ case SSH_S_IFDIR:
+ attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
+ break;
+ default:
+ attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
+ break;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
+ rc = ssh_buffer_unpack(buf, "dd",
+ &attr->atime,
+ &attr->mtime);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
+
+ if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ if (attr->extended_count > 0) {
+ rc = ssh_buffer_unpack(buf, "ss",
+ &attr->extended_type,
+ &attr->extended_data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ attr->extended_count--;
+ }
+ /* just ignore the remaining extensions */
+
+ while (attr->extended_count > 0) {
+ ssh_string tmp1,tmp2;
+ rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
+ if (rc != SSH_OK){
+ goto error;
+ }
+ SAFE_FREE(tmp1);
+ SAFE_FREE(tmp2);
+ attr->extended_count--;
+ }
+ }
+
+ return attr;
+
+error:
+ SSH_STRING_FREE(attr->extended_type);
+ SSH_STRING_FREE(attr->extended_data);
+ SAFE_FREE(attr->name);
+ SAFE_FREE(attr->longname);
+ SAFE_FREE(attr->owner);
+ SAFE_FREE(attr->group);
+ SAFE_FREE(attr);
+ ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+
+ return NULL;
+}
+
+sftp_attributes sftp_parse_attr(sftp_session session,
+ ssh_buffer buf,
+ int expectname)
+{
+ switch (session->version) {
+ case 4:
+ return sftp_parse_attr_4(session, buf, expectname);
+ case 3:
+ case 2:
+ case 1:
+ case 0:
+ return sftp_parse_attr_3(session, buf, expectname);
+ default:
+ ssh_set_error(session->session, SSH_FATAL,
+ "Version %d unsupported by client",
+ session->server_version);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+void sftp_set_error(sftp_session sftp, int errnum)
+{
+ if (sftp != NULL) {
+ sftp->errnum = errnum;
+ }
+}
+
+void sftp_message_free(sftp_message msg)
+{
+ if (msg == NULL) {
+ return;
+ }
+
+ SSH_BUFFER_FREE(msg->payload);
+ SAFE_FREE(msg);
+}
+
+static sftp_request_queue request_queue_new(sftp_message msg)
+{
+ sftp_request_queue queue = NULL;
+
+ queue = calloc(1, sizeof(struct sftp_request_queue_struct));
+ if (queue == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ queue->message = msg;
+
+ return queue;
+}
+
+static void request_queue_free(sftp_request_queue queue)
+{
+ if (queue == NULL) {
+ return;
+ }
+
+ ZERO_STRUCTP(queue);
+ SAFE_FREE(queue);
+}
+
+static int sftp_enqueue(sftp_session sftp, sftp_message msg)
+{
+ sftp_request_queue queue = NULL;
+ sftp_request_queue ptr;
+
+ queue = request_queue_new(msg);
+ if (queue == NULL) {
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Queued msg id %" PRIu32 " type %d",
+ msg->id, msg->packet_type);
+
+ if(sftp->queue == NULL) {
+ sftp->queue = queue;
+ } else {
+ ptr = sftp->queue;
+ while(ptr->next) {
+ ptr=ptr->next; /* find end of linked list */
+ }
+ ptr->next = queue; /* add it on bottom */
+ }
+
+ return 0;
+}
+
+/*
+ * Pulls a message from the queue based on the ID.
+ * Returns NULL if no message has been found.
+ */
+sftp_message sftp_dequeue(sftp_session sftp, uint32_t id)
+{
+ sftp_request_queue prev = NULL;
+ sftp_request_queue queue;
+ sftp_message msg;
+
+ if(sftp->queue == NULL) {
+ return NULL;
+ }
+
+ queue = sftp->queue;
+ while (queue) {
+ if (queue->message->id == id) {
+ /* remove from queue */
+ if (prev == NULL) {
+ sftp->queue = queue->next;
+ } else {
+ prev->next = queue->next;
+ }
+ msg = queue->message;
+ request_queue_free(queue);
+ SSH_LOG(SSH_LOG_PACKET,
+ "Dequeued msg id %" PRIu32 " type %d",
+ msg->id,
+ msg->packet_type);
+ return msg;
+ }
+ prev = queue;
+ queue = queue->next;
+ }
+
+ return NULL;
+}
+
+static sftp_message sftp_get_message(sftp_packet packet)
+{
+ sftp_session sftp = packet->sftp;
+ sftp_message msg = NULL;
+ int rc;
+
+ switch (packet->type) {
+ case SSH_FXP_STATUS:
+ case SSH_FXP_HANDLE:
+ case SSH_FXP_DATA:
+ case SSH_FXP_ATTRS:
+ case SSH_FXP_NAME:
+ case SSH_FXP_EXTENDED_REPLY:
+ break;
+ default:
+ ssh_set_error(packet->sftp->session,
+ SSH_FATAL,
+ "Unknown packet type %d",
+ packet->type);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ msg = calloc(1, sizeof(struct sftp_message_struct));
+ if (msg == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ msg->sftp = packet->sftp;
+ msg->packet_type = packet->type;
+
+ /* Move the payload from the packet to the message */
+ msg->payload = packet->payload;
+ packet->payload = NULL;
+
+ rc = ssh_buffer_unpack(msg->payload, "d", &msg->id);
+ if (rc != SSH_OK) {
+ ssh_set_error(packet->sftp->session, SSH_FATAL,
+ "Invalid packet %d: no ID", packet->type);
+ sftp_message_free(msg);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ SSH_LOG(SSH_LOG_PACKET,
+ "Packet with id %" PRIu32 " type %d",
+ msg->id,
+ msg->packet_type);
+
+ return msg;
+}
+
+int sftp_read_and_dispatch(sftp_session sftp)
+{
+ sftp_packet packet = NULL;
+ sftp_message msg = NULL;
+
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
+ /* something nasty happened reading the packet */
+ return -1;
+ }
+
+ msg = sftp_get_message(packet);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ if (sftp_enqueue(sftp, msg) < 0) {
+ sftp_message_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+sftp_status_message parse_status_msg(sftp_message msg)
+{
+ sftp_status_message status = NULL;
+ int rc;
+
+ if (msg->packet_type != SSH_FXP_STATUS) {
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Not a ssh_fxp_status message passed in!");
+ sftp_set_error(msg->sftp, SSH_FX_BAD_MESSAGE);
+ return NULL;
+ }
+
+ status = calloc(1, sizeof(struct sftp_status_message_struct));
+ if (status == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ status->id = msg->id;
+ rc = ssh_buffer_unpack(msg->payload, "d",
+ &status->status);
+ if (rc != SSH_OK) {
+ SAFE_FREE(status);
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_STATUS message");
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ rc = ssh_buffer_unpack(msg->payload, "ss",
+ &status->errormsg,
+ &status->langmsg);
+
+ if (rc != SSH_OK && msg->sftp->version >= 3) {
+ /* These are mandatory from version 3 */
+ SAFE_FREE(status);
+ ssh_set_error(msg->sftp->session, SSH_FATAL,
+ "Invalid SSH_FXP_STATUS message");
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
+ if (status->errormsg == NULL)
+ status->errormsg = strdup("No error message in packet");
+
+ if (status->langmsg == NULL)
+ status->langmsg = strdup("");
+
+ if (status->errormsg == NULL || status->langmsg == NULL) {
+ ssh_set_error_oom(msg->sftp->session);
+ sftp_set_error(msg->sftp, SSH_FX_FAILURE);
+ status_msg_free(status);
+ return NULL;
+ }
+
+ return status;
+}
+
+void status_msg_free(sftp_status_message status)
+{
+ if (status == NULL) {
+ return;
+ }
+
+ SAFE_FREE(status->errormsg);
+ SAFE_FREE(status->langmsg);
+ SAFE_FREE(status);
+}
+
+#endif /* WITH_SFTP */
diff --git a/src/sftpserver.c b/src/sftpserver.c
index 9117f155..7d8070b1 100644
--- a/src/sftpserver.c
+++ b/src/sftpserver.c
@@ -3,7 +3,8 @@
*
* This file is part of the SSH Library
*
- * Copyright (c) 2005 by Aris Adamantiadis
+ * Copyright (c) 2005 Aris Adamantiadis
+ * Copyright (c) 2022 Zeyu Sheng <shengzeyu19_98@163.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -23,16 +24,23 @@
#include "config.h"
-#include <stdio.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <dirent.h>
+#include <sys/statvfs.h>
#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <time.h>
#include "libssh/libssh.h"
#include "libssh/sftp.h"
#include "libssh/sftp_priv.h"
+#include "libssh/sftpserver.h"
#include "libssh/ssh2.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
@@ -40,518 +48,1720 @@
#define SFTP_HANDLES 256
-sftp_client_message sftp_get_client_message(sftp_session sftp) {
- ssh_session session = sftp->session;
- sftp_packet packet;
- sftp_client_message msg;
- ssh_buffer payload;
- int rc;
+#define MAX_ENTRIES_NUM_IN_PACKET 50
+#define MAX_LONG_NAME_LEN 350
- msg = malloc(sizeof (struct sftp_client_message_struct));
- if (msg == NULL) {
- ssh_set_error_oom(session);
- return NULL;
- }
- ZERO_STRUCTP(msg);
+static sftp_client_message
+sftp_make_client_message(sftp_session sftp, sftp_packet packet)
+{
+ ssh_session session = sftp->session;
+ sftp_client_message msg = NULL;
+ ssh_buffer payload = NULL;
+ int rc;
+ int version;
+
+ msg = calloc(1, sizeof(struct sftp_client_message_struct));
+ if (msg == NULL) {
+ ssh_set_error_oom(session);
+ return NULL;
+ }
+
+ payload = packet->payload;
+ msg->type = packet->type;
+ msg->sftp = sftp;
+
+ /* take a copy of the whole packet */
+ msg->complete_message = ssh_buffer_new();
+ if (msg->complete_message == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
- packet = sftp_packet_read(sftp);
- if (packet == NULL) {
- ssh_set_error_oom(session);
+ rc = ssh_buffer_add_data(msg->complete_message,
+ ssh_buffer_get(payload),
+ ssh_buffer_get_len(payload));
+ if (rc < 0) {
+ goto error;
+ }
+
+ if (msg->type != SSH_FXP_INIT) {
+ rc = ssh_buffer_get_u32(payload, &msg->id);
+ if (rc != sizeof(uint32_t)) {
+ goto error;
+ }
+ }
+
+ switch (msg->type) {
+ case SSH_FXP_INIT:
+ rc = ssh_buffer_unpack(payload,
+ "d",
+ &version);
+ if (rc != SSH_OK) {
+ printf("unpack init failed!\n");
+ goto error;
+ }
+ version = ntohl(version);
+ sftp->client_version = version;
+ break;
+ case SSH_FXP_CLOSE:
+ case SSH_FXP_READDIR:
+ msg->handle = ssh_buffer_get_ssh_string(payload);
+ if (msg->handle == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_READ:
+ rc = ssh_buffer_unpack(payload,
+ "Sqd",
+ &msg->handle,
+ &msg->offset,
+ &msg->len);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_WRITE:
+ rc = ssh_buffer_unpack(payload,
+ "SqS",
+ &msg->handle,
+ &msg->offset,
+ &msg->data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_REMOVE:
+ case SSH_FXP_RMDIR:
+ case SSH_FXP_OPENDIR:
+ case SSH_FXP_READLINK:
+ case SSH_FXP_REALPATH:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_RENAME:
+ case SSH_FXP_SYMLINK:
+ rc = ssh_buffer_unpack(payload,
+ "sS",
+ &msg->filename,
+ &msg->data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_MKDIR:
+ case SSH_FXP_SETSTAT:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ msg->attr = sftp_parse_attr(sftp, payload, 0);
+ if (msg->attr == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_FSETSTAT:
+ msg->handle = ssh_buffer_get_ssh_string(payload);
+ if (msg->handle == NULL) {
+ goto error;
+ }
+ msg->attr = sftp_parse_attr(sftp, payload, 0);
+ if (msg->attr == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_LSTAT:
+ case SSH_FXP_STAT:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ if (sftp->version > 3) {
+ ssh_buffer_unpack(payload, "d", &msg->flags);
+ }
+ break;
+ case SSH_FXP_OPEN:
+ rc = ssh_buffer_unpack(payload,
+ "sd",
+ &msg->filename,
+ &msg->flags);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ msg->attr = sftp_parse_attr(sftp, payload, 0);
+ if (msg->attr == NULL) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_FSTAT:
+ rc = ssh_buffer_unpack(payload,
+ "S",
+ &msg->handle);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ break;
+ case SSH_FXP_EXTENDED:
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->submessage);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+
+ if (strcmp(msg->submessage, "hardlink@openssh.com") == 0 ||
+ strcmp(msg->submessage, "posix-rename@openssh.com") == 0) {
+ rc = ssh_buffer_unpack(payload,
+ "sS",
+ &msg->filename,
+ &msg->data);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ } else if (strcmp(msg->submessage, "statvfs@openssh.com") == 0 ){
+ rc = ssh_buffer_unpack(payload,
+ "s",
+ &msg->filename);
+ if (rc != SSH_OK) {
+ goto error;
+ }
+ }
+ break;
+ default:
+ ssh_set_error(sftp->session, SSH_FATAL,
+ "Received unhandled sftp message %d", msg->type);
+ goto error;
+ }
+
+ return msg;
+
+error:
sftp_client_message_free(msg);
return NULL;
- }
-
- payload = packet->payload;
- msg->type = packet->type;
- msg->sftp = sftp;
+}
- /* take a copy of the whole packet */
- msg->complete_message = ssh_buffer_new();
- if (msg->complete_message == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
+sftp_client_message sftp_get_client_message(sftp_session sftp)
+{
+ ssh_session session = sftp->session;
+ sftp_packet packet;
- rc = ssh_buffer_add_data(msg->complete_message,
- ssh_buffer_get(payload),
- ssh_buffer_get_len(payload));
- if (rc < 0) {
+ packet = sftp_packet_read(sftp);
+ if (packet == NULL) {
ssh_set_error_oom(session);
- sftp_client_message_free(msg);
return NULL;
- }
+ }
+ return sftp_make_client_message(sftp, packet);
+}
- ssh_buffer_get_u32(payload, &msg->id);
+/**
+ * @brief Get the client message from a sftp packet.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @return The pointer to the generated sftp client message.
+ */
+static sftp_client_message
+sftp_get_client_message_from_packet(sftp_session sftp)
+{
+ sftp_packet packet = NULL;
- switch(msg->type) {
- case SSH_FXP_CLOSE:
- case SSH_FXP_READDIR:
- msg->handle = ssh_buffer_get_ssh_string(payload);
- if (msg->handle == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_READ:
- rc = ssh_buffer_unpack(payload,
- "Sqd",
- &msg->handle,
- &msg->offset,
- &msg->len);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_WRITE:
- rc = ssh_buffer_unpack(payload,
- "SqS",
- &msg->handle,
- &msg->offset,
- &msg->data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_REMOVE:
- case SSH_FXP_RMDIR:
- case SSH_FXP_OPENDIR:
- case SSH_FXP_READLINK:
- case SSH_FXP_REALPATH:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->filename);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_RENAME:
- case SSH_FXP_SYMLINK:
- rc = ssh_buffer_unpack(payload,
- "sS",
- &msg->filename,
- &msg->data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_MKDIR:
- case SSH_FXP_SETSTAT:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->filename);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- msg->attr = sftp_parse_attr(sftp, payload, 0);
- if (msg->attr == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_FSETSTAT:
- msg->handle = ssh_buffer_get_ssh_string(payload);
- if (msg->handle == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- msg->attr = sftp_parse_attr(sftp, payload, 0);
- if (msg->attr == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_LSTAT:
- case SSH_FXP_STAT:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->filename);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- if(sftp->version > 3) {
- ssh_buffer_unpack(payload, "d", &msg->flags);
- }
- break;
- case SSH_FXP_OPEN:
- rc = ssh_buffer_unpack(payload,
- "sd",
- &msg->filename,
- &msg->flags);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- msg->attr = sftp_parse_attr(sftp, payload, 0);
- if (msg->attr == NULL) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_FSTAT:
- rc = ssh_buffer_unpack(payload,
- "S",
- &msg->handle);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- break;
- case SSH_FXP_EXTENDED:
- rc = ssh_buffer_unpack(payload,
- "s",
- &msg->submessage);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
+ packet = sftp->read_packet;
+ if (packet == NULL) {
return NULL;
- }
-
- if (strcmp(msg->submessage, "hardlink@openssh.com") == 0 ||
- strcmp(msg->submessage, "posix-rename@openssh.com") == 0) {
- rc = ssh_buffer_unpack(payload,
- "sS",
- &msg->filename,
- &msg->data);
- if (rc != SSH_OK) {
- ssh_set_error_oom(session);
- sftp_client_message_free(msg);
- return NULL;
- }
- }
- break;
- default:
- ssh_set_error(sftp->session, SSH_FATAL,
- "Received unhandled sftp message %d", msg->type);
- sftp_client_message_free(msg);
- return NULL;
- }
-
- return msg;
+ }
+ return sftp_make_client_message(sftp, packet);
}
-/* Send an sftp client message. Can be used in cas of proxying */
-int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){
- return sftp_packet_write(sftp, msg->type, msg->complete_message);
+/* Send an sftp client message. Can be used in case of proxying */
+int sftp_send_client_message(sftp_session sftp, sftp_client_message msg)
+{
+ return sftp_packet_write(sftp, msg->type, msg->complete_message);
}
-uint8_t sftp_client_message_get_type(sftp_client_message msg){
- return msg->type;
+uint8_t sftp_client_message_get_type(sftp_client_message msg)
+{
+ return msg->type;
}
-const char *sftp_client_message_get_filename(sftp_client_message msg){
- return msg->filename;
+const char *sftp_client_message_get_filename(sftp_client_message msg)
+{
+ return msg->filename;
}
-void sftp_client_message_set_filename(sftp_client_message msg, const char *newname){
- free(msg->filename);
- msg->filename = strdup(newname);
+void
+sftp_client_message_set_filename(sftp_client_message msg, const char *newname)
+{
+ free(msg->filename);
+ msg->filename = strdup(newname);
}
-const char *sftp_client_message_get_data(sftp_client_message msg){
- if (msg->str_data == NULL)
- msg->str_data = ssh_string_to_char(msg->data);
- return msg->str_data;
+const char *sftp_client_message_get_data(sftp_client_message msg)
+{
+ if (msg->str_data == NULL)
+ msg->str_data = ssh_string_to_char(msg->data);
+ return msg->str_data;
}
-uint32_t sftp_client_message_get_flags(sftp_client_message msg){
- return msg->flags;
+uint32_t sftp_client_message_get_flags(sftp_client_message msg)
+{
+ return msg->flags;
}
-const char *sftp_client_message_get_submessage(sftp_client_message msg){
- return msg->submessage;
+const char *sftp_client_message_get_submessage(sftp_client_message msg)
+{
+ return msg->submessage;
}
-void sftp_client_message_free(sftp_client_message msg) {
- if (msg == NULL) {
- return;
- }
+void sftp_client_message_free(sftp_client_message msg)
+{
+ if (msg == NULL) {
+ return;
+ }
- SAFE_FREE(msg->filename);
- SAFE_FREE(msg->submessage);
- SSH_STRING_FREE(msg->data);
- SSH_STRING_FREE(msg->handle);
- sftp_attributes_free(msg->attr);
- SSH_BUFFER_FREE(msg->complete_message);
- SAFE_FREE(msg->str_data);
- ZERO_STRUCTP(msg);
- SAFE_FREE(msg);
+ SAFE_FREE(msg->filename);
+ SAFE_FREE(msg->submessage);
+ SSH_STRING_FREE(msg->data);
+ SSH_STRING_FREE(msg->handle);
+ sftp_attributes_free(msg->attr);
+ SSH_BUFFER_FREE(msg->complete_message);
+ SAFE_FREE(msg->str_data);
+ ZERO_STRUCTP(msg);
+ SAFE_FREE(msg);
}
-int sftp_reply_name(sftp_client_message msg, const char *name,
- sftp_attributes attr) {
- ssh_buffer out;
- ssh_string file;
+int
+sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
+{
+ ssh_buffer out;
+ ssh_string file;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- file = ssh_string_from_char(name);
- if (file == NULL) {
- SSH_BUFFER_FREE(out);
- return -1;
- }
-
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, htonl(1)) < 0 ||
- ssh_buffer_add_ssh_string(out, file) < 0 ||
- ssh_buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */
- buffer_add_attributes(out, attr) < 0 ||
- sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
+ file = ssh_string_from_char(name);
+ if (file == NULL) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending name %s", ssh_string_get_char(file));
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, htonl(1)) < 0 ||
+ ssh_buffer_add_ssh_string(out, file) < 0 ||
+ ssh_buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */
+ buffer_add_attributes(out, attr) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
+ SSH_BUFFER_FREE(out);
+ SSH_STRING_FREE(file);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
SSH_STRING_FREE(file);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- SSH_STRING_FREE(file);
- return 0;
+ return 0;
}
-int sftp_reply_handle(sftp_client_message msg, ssh_string handle){
- ssh_buffer out;
+int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_ssh_string(out, handle) < 0 ||
- sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) {
+ ssh_log_hexdump("Sending handle:",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_ssh_string(out, handle) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- return 0;
+ return 0;
}
-int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) {
- ssh_buffer out;
+int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- buffer_add_attributes(out, attr) < 0 ||
- sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending attr");
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ buffer_add_attributes(out, attr) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- return 0;
+ return 0;
}
-int sftp_reply_names_add(sftp_client_message msg, const char *file,
- const char *longname, sftp_attributes attr) {
- ssh_string name;
+int
+sftp_reply_names_add(sftp_client_message msg, const char *file,
+ const char *longname, sftp_attributes attr)
+{
+ ssh_string name;
- name = ssh_string_from_char(file);
- if (name == NULL) {
- return -1;
- }
+ name = ssh_string_from_char(file);
+ if (name == NULL) {
+ return -1;
+ }
- if (msg->attrbuf == NULL) {
- msg->attrbuf = ssh_buffer_new();
if (msg->attrbuf == NULL) {
- SSH_STRING_FREE(name);
- return -1;
+ msg->attrbuf = ssh_buffer_new();
+ if (msg->attrbuf == NULL) {
+ SSH_STRING_FREE(name);
+ return -1;
+ }
}
- }
- if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0) {
- SSH_STRING_FREE(name);
- return -1;
- }
+ if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0) {
+ SSH_STRING_FREE(name);
+ return -1;
+ }
- SSH_STRING_FREE(name);
- name = ssh_string_from_char(longname);
- if (name == NULL) {
- return -1;
- }
- if (ssh_buffer_add_ssh_string(msg->attrbuf,name) < 0 ||
- buffer_add_attributes(msg->attrbuf,attr) < 0) {
SSH_STRING_FREE(name);
- return -1;
- }
- SSH_STRING_FREE(name);
- msg->attr_num++;
+ name = ssh_string_from_char(longname);
+ if (name == NULL) {
+ return -1;
+ }
+ if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0 ||
+ buffer_add_attributes(msg->attrbuf, attr) < 0) {
+ SSH_STRING_FREE(name);
+ return -1;
+ }
+ SSH_STRING_FREE(name);
+ msg->attr_num++;
- return 0;
+ return 0;
}
-int sftp_reply_names(sftp_client_message msg) {
- ssh_buffer out;
+int sftp_reply_names(sftp_client_message msg)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- SSH_BUFFER_FREE(msg->attrbuf);
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ SSH_BUFFER_FREE(msg->attrbuf);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d names", msg->attr_num);
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
+ ssh_buffer_add_data(out, ssh_buffer_get(msg->attrbuf),
+ ssh_buffer_get_len(msg->attrbuf)) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
+ SSH_BUFFER_FREE(out);
+ SSH_BUFFER_FREE(msg->attrbuf);
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
- ssh_buffer_add_data(out, ssh_buffer_get(msg->attrbuf),
- ssh_buffer_get_len(msg->attrbuf)) < 0 ||
- sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
SSH_BUFFER_FREE(out);
SSH_BUFFER_FREE(msg->attrbuf);
- return -1;
- }
-
- SSH_BUFFER_FREE(out);
- SSH_BUFFER_FREE(msg->attrbuf);
- msg->attr_num = 0;
- msg->attrbuf = NULL;
+ msg->attr_num = 0;
+ msg->attrbuf = NULL;
- return 0;
+ return 0;
}
-int sftp_reply_status(sftp_client_message msg, uint32_t status,
- const char *message) {
- ssh_buffer out;
- ssh_string s;
+int
+sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
+{
+ ssh_buffer out;
+ ssh_string s;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- s = ssh_string_from_char(message ? message : "");
- if (s == NULL) {
- SSH_BUFFER_FREE(out);
- return -1;
- }
+ s = ssh_string_from_char(message ? message : "");
+ if (s == NULL) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending status %d, message: %s", status,
+ ssh_string_get_char(s));
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, htonl(status)) < 0 ||
+ ssh_buffer_add_ssh_string(out, s) < 0 ||
+ ssh_buffer_add_u32(out, 0) < 0 || /* language string */
+ sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) {
+ SSH_BUFFER_FREE(out);
+ SSH_STRING_FREE(s);
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, htonl(status)) < 0 ||
- ssh_buffer_add_ssh_string(out, s) < 0 ||
- ssh_buffer_add_u32(out, 0) < 0 || /* language string */
- sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) {
SSH_BUFFER_FREE(out);
SSH_STRING_FREE(s);
- return -1;
- }
-
- SSH_BUFFER_FREE(out);
- SSH_STRING_FREE(s);
- return 0;
+ return 0;
}
-int sftp_reply_data(sftp_client_message msg, const void *data, int len) {
- ssh_buffer out;
+int sftp_reply_data(sftp_client_message msg, const void *data, int len)
+{
+ ssh_buffer out;
- out = ssh_buffer_new();
- if (out == NULL) {
- return -1;
- }
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
- if (ssh_buffer_add_u32(out, msg->id) < 0 ||
- ssh_buffer_add_u32(out, ntohl(len)) < 0 ||
- ssh_buffer_add_data(out, data, len) < 0 ||
- sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending data, length: %d", len);
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u32(out, ntohl(len)) < 0 ||
+ ssh_buffer_add_data(out, data, len) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) {
+ SSH_BUFFER_FREE(out);
+ return -1;
+ }
SSH_BUFFER_FREE(out);
- return -1;
- }
- SSH_BUFFER_FREE(out);
- return 0;
+ return 0;
+}
+
+/**
+ * @brief Handle the statvfs request, return information the mounted file system.
+ *
+ * @param msg The sftp client message.
+ *
+ * @param st The statvfs state of target file.
+ *
+ * @return 0 on success, < 0 on error with ssh and sftp error set.
+ *
+ * @see sftp_get_error()
+ */
+static int
+sftp_reply_statvfs(sftp_client_message msg, sftp_statvfs_t st)
+{
+ int ret = 0;
+ ssh_buffer out;
+ out = ssh_buffer_new();
+ if (out == NULL) {
+ return -1;
+ }
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending statvfs reply");
+
+ if (ssh_buffer_add_u32(out, msg->id) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_bsize)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_frsize)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_blocks)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_bfree)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_bavail)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_files)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_ffree)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_favail)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_fsid)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_flag)) < 0 ||
+ ssh_buffer_add_u64(out, ntohll(st->f_namemax)) < 0 ||
+ sftp_packet_write(msg->sftp, SSH_FXP_EXTENDED_REPLY, out) < 0) {
+ ret = -1;
+ }
+ SSH_BUFFER_FREE(out);
+
+ return ret;
+}
+
+int sftp_reply_version(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_session session = sftp->session;
+ int version;
+ ssh_buffer reply;
+ int rc;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Sending version packet");
+
+ version = sftp->client_version;
+ reply = ssh_buffer_new();
+ if (reply == NULL) {
+ ssh_set_error_oom(session);
+ return -1;
+ }
+
+ rc = ssh_buffer_pack(reply, "dssssss",
+ LIBSFTP_VERSION,
+ "posix-rename@openssh.com",
+ "1",
+ "hardlink@openssh.com",
+ "1",
+ "statvfs@openssh.com",
+ "2");
+ if (rc != SSH_OK) {
+ ssh_set_error_oom(session);
+ SSH_BUFFER_FREE(reply);
+ return -1;
+ }
+
+ rc = sftp_packet_write(sftp, SSH_FXP_VERSION, reply);
+ if (rc < 0) {
+ SSH_BUFFER_FREE(reply);
+ return -1;
+ }
+ SSH_BUFFER_FREE(reply);
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Server version sent");
+
+ if (version > LIBSFTP_VERSION) {
+ sftp->version = LIBSFTP_VERSION;
+ } else {
+ sftp->version = version;
+ }
+
+ return SSH_OK;
}
+
/*
* This function will return you a new handle to give the client.
* the function accepts an info that can be retrieved later with
* the handle. Care is given that a corrupted handle won't give a
* valid info (or worse).
*/
-ssh_string sftp_handle_alloc(sftp_session sftp, void *info) {
- ssh_string ret;
- uint32_t val;
- uint32_t i;
+ssh_string sftp_handle_alloc(sftp_session sftp, void *info)
+{
+ ssh_string ret;
+ uint32_t val;
+ uint32_t i;
- if (sftp->handles == NULL) {
- sftp->handles = calloc(SFTP_HANDLES, sizeof(void *));
if (sftp->handles == NULL) {
- return NULL;
+ sftp->handles = calloc(SFTP_HANDLES, sizeof(void *));
+ if (sftp->handles == NULL) {
+ return NULL;
+ }
}
- }
- for (i = 0; i < SFTP_HANDLES; i++) {
- if (sftp->handles[i] == NULL) {
- break;
+ for (i = 0; i < SFTP_HANDLES; i++) {
+ if (sftp->handles[i] == NULL) {
+ break;
+ }
}
- }
- if (i == SFTP_HANDLES) {
- return NULL; /* no handle available */
- }
+ if (i == SFTP_HANDLES) {
+ return NULL; /* no handle available */
+ }
- val = i;
- ret = ssh_string_new(4);
- if (ret == NULL) {
- return NULL;
- }
+ val = i;
+ ret = ssh_string_new(4);
+ if (ret == NULL) {
+ return NULL;
+ }
- memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
- sftp->handles[i] = info;
+ memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
+ sftp->handles[i] = info;
- return ret;
+ return ret;
}
-void *sftp_handle(sftp_session sftp, ssh_string handle){
- uint32_t val;
+void *sftp_handle(sftp_session sftp, ssh_string handle)
+{
+ uint32_t val;
- if (sftp->handles == NULL) {
- return NULL;
- }
+ if (sftp->handles == NULL) {
+ return NULL;
+ }
- if (ssh_string_len(handle) != sizeof(uint32_t)) {
- return NULL;
- }
+ if (ssh_string_len(handle) != sizeof(uint32_t)) {
+ return NULL;
+ }
- memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
+ memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
- if (val > SFTP_HANDLES) {
- return NULL;
- }
+ if (val > SFTP_HANDLES) {
+ return NULL;
+ }
+
+ return sftp->handles[val];
+}
+
+void sftp_handle_remove(sftp_session sftp, void *handle)
+{
+ int i;
+
+ for (i = 0; i < SFTP_HANDLES; i++) {
+ if (sftp->handles[i] == handle) {
+ sftp->handles[i] = NULL;
+ break;
+ }
+ }
+}
+
+/* Default SFTP handlers */
+
+static const char *
+ssh_str_error(int u_errno)
+{
+ switch (u_errno) {
+ case SSH_FX_NO_SUCH_FILE:
+ return "No such file";
+ case SSH_FX_PERMISSION_DENIED:
+ return "Permission denied";
+ case SSH_FX_BAD_MESSAGE:
+ return "Bad message";
+ case SSH_FX_OP_UNSUPPORTED:
+ return "Operation not supported";
+ default:
+ return "Operation failed";
+ }
+}
+
+static int
+unix_errno_to_ssh_stat(int u_errno)
+{
+ int ret = SSH_OK;
+ switch (u_errno) {
+ case 0:
+ break;
+ case ENOENT:
+ case ENOTDIR:
+ case EBADF:
+ case ELOOP:
+ ret = SSH_FX_NO_SUCH_FILE;
+ break;
+ case EPERM:
+ case EACCES:
+ case EFAULT:
+ ret = SSH_FX_PERMISSION_DENIED;
+ break;
+ case ENAMETOOLONG:
+ case EINVAL:
+ ret = SSH_FX_BAD_MESSAGE;
+ break;
+ case ENOSYS:
+ ret = SSH_FX_OP_UNSUPPORTED;
+ break;
+ default:
+ ret = SSH_FX_FAILURE;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+stat_to_filexfer_attrib(const struct stat *z_st, struct sftp_attributes_struct *z_attr)
+{
+ z_attr->flags = 0 | (uint32_t)SSH_FILEXFER_ATTR_SIZE;
+ z_attr->size = z_st->st_size;
+
+ z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_UIDGID;
+ z_attr->uid = z_st->st_uid;
+ z_attr->gid = z_st->st_gid;
- return sftp->handles[val];
+ z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS;
+ z_attr->permissions = z_st->st_mode;
+
+ z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_ACMODTIME;
+ z_attr->atime = z_st->st_atime;
+ z_attr->mtime = z_st->st_mtime;
+}
+
+static void
+clear_filexfer_attrib(struct sftp_attributes_struct *z_attr)
+{
+ z_attr->flags = 0;
+ z_attr->size = 0;
+ z_attr->uid = 0;
+ z_attr->gid = 0;
+ z_attr->permissions = 0;
+ z_attr->atime = 0;
+ z_attr->mtime = 0;
+}
+
+#ifndef _WIN32
+/* internal */
+enum sftp_handle_type
+{
+ SFTP_NULL_HANDLE,
+ SFTP_DIR_HANDLE,
+ SFTP_FILE_HANDLE
+};
+
+struct sftp_handle
+{
+ enum sftp_handle_type type;
+ int fd;
+ DIR *dirp;
+ char *name;
+};
+
+SSH_SFTP_CALLBACK(process_unsupposed);
+SSH_SFTP_CALLBACK(process_open);
+SSH_SFTP_CALLBACK(process_read);
+SSH_SFTP_CALLBACK(process_write);
+SSH_SFTP_CALLBACK(process_close);
+SSH_SFTP_CALLBACK(process_opendir);
+SSH_SFTP_CALLBACK(process_readdir);
+SSH_SFTP_CALLBACK(process_rmdir);
+SSH_SFTP_CALLBACK(process_realpath);
+SSH_SFTP_CALLBACK(process_mkdir);
+SSH_SFTP_CALLBACK(process_lstat);
+SSH_SFTP_CALLBACK(process_stat);
+SSH_SFTP_CALLBACK(process_readlink);
+SSH_SFTP_CALLBACK(process_symlink);
+SSH_SFTP_CALLBACK(process_remove);
+SSH_SFTP_CALLBACK(process_extended_statvfs);
+
+const struct sftp_message_handler message_handlers[] = {
+ {"open", NULL, SSH_FXP_OPEN, process_open},
+ {"close", NULL, SSH_FXP_CLOSE, process_close},
+ {"read", NULL, SSH_FXP_READ, process_read},
+ {"write", NULL, SSH_FXP_WRITE, process_write},
+ {"lstat", NULL, SSH_FXP_LSTAT, process_lstat},
+ {"fstat", NULL, SSH_FXP_FSTAT, process_unsupposed},
+ {"setstat", NULL, SSH_FXP_SETSTAT, process_unsupposed},
+ {"fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed},
+ {"opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
+ {"readdir", NULL, SSH_FXP_READDIR, process_readdir},
+ {"remove", NULL, SSH_FXP_REMOVE, process_remove},
+ {"mkdir", NULL, SSH_FXP_MKDIR, process_mkdir},
+ {"rmdir", NULL, SSH_FXP_RMDIR, process_rmdir},
+ {"realpath", NULL, SSH_FXP_REALPATH, process_realpath},
+ {"stat", NULL, SSH_FXP_STAT, process_stat},
+ {"rename", NULL, SSH_FXP_RENAME, process_unsupposed},
+ {"readlink", NULL, SSH_FXP_READLINK, process_readlink},
+ {"symlink", NULL, SSH_FXP_SYMLINK, process_symlink},
+ {"init", NULL, SSH_FXP_INIT, sftp_reply_version},
+ {NULL, NULL, 0, NULL},
+};
+
+const struct sftp_message_handler extended_handlers[] = {
+ /* here are some extended type handlers */
+ {"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
+ {NULL, NULL, 0, NULL},
+};
+
+static int
+process_open(sftp_client_message client_msg)
+{
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ uint32_t msg_flag = sftp_client_message_get_flags(client_msg);
+ uint32_t mode = client_msg->attr->permissions;
+ ssh_string handle_s = NULL;
+ struct sftp_handle *h = NULL;
+ int file_flag;
+ int fd = -1;
+ int status;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing open: filename %s, mode=0%o" PRIu32,
+ filename, mode);
+
+ if (((msg_flag & (uint32_t)SSH_FXF_READ) == SSH_FXF_READ) &&
+ ((msg_flag & (uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE)) {
+ file_flag = O_RDWR; // file must exist
+ if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
+ file_flag |= O_CREAT;
+ } else if ((msg_flag & (uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE) {
+ file_flag = O_WRONLY;
+ if ((msg_flag & (uint32_t)SSH_FXF_APPEND) == SSH_FXF_APPEND)
+ file_flag |= O_APPEND;
+ if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
+ file_flag |= O_CREAT;
+ } else if ((msg_flag & (uint32_t)SSH_FXF_READ) == SSH_FXF_READ) {
+ file_flag = O_RDONLY;
+ } else {
+ SSH_LOG(SSH_LOG_PROTOCOL, "undefined message flag: %" PRIu32, msg_flag);
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, "Flag error");
+ return SSH_ERROR;
+ }
+
+ fd = open(filename, file_flag, mode);
+ if (fd == -1) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "error open file with error: %s",
+ strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, "Write error");
+ return SSH_ERROR;
+ }
+
+ h = calloc(1, sizeof (struct sftp_handle));
+ if (h == NULL) {
+ close(fd);
+ SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE,
+ "Failed to allocate new handle");
+ return SSH_ERROR;
+ }
+ h->fd = fd;
+ h->type = SFTP_FILE_HANDLE;
+ handle_s = sftp_handle_alloc(client_msg->sftp, h);
+ if (handle_s != NULL) {
+ sftp_reply_handle(client_msg, handle_s);
+ ssh_string_free(handle_s);
+ } else {
+ close(fd);
+ SSH_LOG(SSH_LOG_PROTOCOL, "Failed to allocate handle");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE,
+ "Failed to allocate handle");
+ }
+
+ return SSH_OK;
+}
+
+static int
+process_read(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ ssize_t allreadn = 0;
+ int fd = -1;
+ char *buffer = NULL;
+ int rv;
+
+ ssh_log_hexdump("Processing read: handle:",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, handle);
+ if (h->type == SFTP_FILE_HANDLE) {
+ fd = h->fd;
+ }
+
+ if (fd < 0) {
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "invalid fd (%d) received from handle", fd);
+ return SSH_ERROR;
+ }
+ rv = lseek(fd, client_msg->offset, SEEK_SET);
+ if (rv == -1) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL,
+ "error seeking file fd: %d at offset: %" PRIu64,
+ fd, client_msg->offset);
+ return SSH_ERROR;
+ }
+
+ buffer = malloc(client_msg->len);
+ if (buffer == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "Failed to allocate memory for read data");
+ return SSH_ERROR;
+ }
+ do {
+ ssize_t readn = read(fd, buffer + allreadn, client_msg->len - allreadn);
+ if (readn < 0) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "read file error!");
+ free(buffer);
+ return SSH_ERROR;
+ } else if (readn == 0) {
+ /* no more data to read, EOF ? */
+ break;
+ }
+ allreadn += readn;
+ } while (allreadn < (ssize_t)client_msg->len);
+
+ if (allreadn > 0) {
+ sftp_reply_data(client_msg, buffer, allreadn);
+ } else {
+ sftp_reply_status(client_msg, SSH_FX_EOF, NULL);
+ }
+
+ free(buffer);
+ return SSH_OK;
+}
+
+static int
+process_write(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ ssize_t written = 0;
+ int fd = -1;
+ const char *msg_data = NULL;
+ uint32_t len;
+ int rv;
+
+ ssh_log_hexdump("Processing write: handle",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, handle);
+ if (h->type == SFTP_FILE_HANDLE) {
+ fd = h->fd;
+ }
+ if (fd < 0) {
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "write file fd error!");
+ return SSH_ERROR;
+ }
+
+ msg_data = ssh_string_get_char(client_msg->data);
+ len = ssh_string_len(client_msg->data);
+
+ rv = lseek(fd, client_msg->offset, SEEK_SET);
+ if (rv == -1) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file at offset: %" PRIu64,
+ client_msg->offset);
+ }
+ do {
+ rv = write(fd, msg_data + written, len - written);
+ if (rv < 0) {
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error");
+ SSH_LOG(SSH_LOG_PROTOCOL, "file write error!");
+ return SSH_ERROR;
+ }
+ written += rv;
+ } while (written < (int)len);
+
+ sftp_reply_status(client_msg, SSH_FX_OK, NULL);
+
+ return SSH_OK;
+}
+
+static int
+process_close(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ int ret;
+
+ ssh_log_hexdump("Processing close: handle:",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, handle);
+ if (h->type == SFTP_FILE_HANDLE) {
+ int fd = h->fd;
+ close(fd);
+ ret = SSH_OK;
+ } else if (h->type == SFTP_DIR_HANDLE) {
+ DIR *dir = h->dirp;
+ closedir(dir);
+ ret = SSH_OK;
+ } else {
+ ret = SSH_ERROR;
+ }
+ SAFE_FREE(h->name);
+ sftp_handle_remove(sftp, h);
+ SAFE_FREE(h);
+
+ if (ret == SSH_OK) {
+ sftp_reply_status(client_msg, SSH_FX_OK, NULL);
+ } else {
+ SSH_LOG(SSH_LOG_PROTOCOL, "closing file failed");
+ sftp_reply_status(client_msg, SSH_FX_BAD_MESSAGE, "Invalid handle");
+ }
+
+ return SSH_OK;
+}
+
+static int
+process_opendir(sftp_client_message client_msg)
+{
+ DIR *dir = NULL;
+ const char *dir_name = sftp_client_message_get_filename(client_msg);
+ ssh_string handle_s = NULL;
+ struct sftp_handle *h = NULL;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing opendir %s", dir_name);
+
+ dir = opendir(dir_name);
+ if (dir == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "No such directory");
+ return SSH_ERROR;
+ }
+
+ h = calloc(1, sizeof (struct sftp_handle));
+ if (h == NULL) {
+ closedir(dir);
+ SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE,
+ "Failed to allocate new handle");
+ return SSH_ERROR;
+ }
+ h->dirp = dir;
+ h->name = strdup(dir_name);
+ h->type = SFTP_DIR_HANDLE;
+ handle_s = sftp_handle_alloc(client_msg->sftp, h);
+
+ if (handle_s != NULL) {
+ sftp_reply_handle(client_msg, handle_s);
+ ssh_string_free(handle_s);
+ } else {
+ closedir(dir);
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
+ }
+
+ return SSH_OK;
+}
+
+static int
+readdir_long_name(char *z_file_name, struct stat *z_st, char *z_long_name)
+{
+ char tmpbuf[MAX_LONG_NAME_LEN];
+ char time[50];
+ char *ptr = z_long_name;
+ int mode = z_st->st_mode;
+
+ *ptr = '\0';
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ *ptr++ = 'd';
+ break;
+ default:
+ *ptr++ = '-';
+ break;
+ }
+
+ /* user */
+ if (mode & 0400)
+ *ptr++ = 'r';
+ else
+ *ptr++ = '-';
+
+ if (mode & 0200)
+ *ptr++ = 'w';
+ else
+ *ptr++ = '-';
+
+ if (mode & 0100) {
+ if (mode & S_ISUID)
+ *ptr++ = 's';
+ else
+ *ptr++ = 'x';
+ } else
+ *ptr++ = '-';
+
+ /* group */
+ if (mode & 040)
+ *ptr++ = 'r';
+ else
+ *ptr++ = '-';
+ if (mode & 020)
+ *ptr++ = 'w';
+ else
+ *ptr++ = '-';
+ if (mode & 010)
+ *ptr++ = 'x';
+ else
+ *ptr++ = '-';
+
+ /* other */
+ if (mode & 04)
+ *ptr++ = 'r';
+ else
+ *ptr++ = '-';
+ if (mode & 02)
+ *ptr++ = 'w';
+ else
+ *ptr++ = '-';
+ if (mode & 01)
+ *ptr++ = 'x';
+ else
+ *ptr++ = '-';
+
+ *ptr++ = ' ';
+ *ptr = '\0';
+
+ snprintf(tmpbuf, sizeof(tmpbuf), "%3d %d %d %d", (int)z_st->st_nlink,
+ (int)z_st->st_uid, (int)z_st->st_gid, (int)z_st->st_size);
+ strcat(z_long_name, tmpbuf);
+
+ ctime_r(&z_st->st_mtime, time);
+ if ((ptr = strchr(time, '\n'))) {
+ *ptr = '\0';
+ }
+ snprintf(tmpbuf, sizeof(tmpbuf), " %s %s", time + 4, z_file_name);
+ strcat(z_long_name, tmpbuf);
+
+ return SSH_OK;
+}
+
+static int
+process_readdir(sftp_client_message client_msg)
+{
+ sftp_session sftp = client_msg->sftp;
+ ssh_string handle = client_msg->handle;
+ struct sftp_handle *h = NULL;
+ int ret = SSH_OK;
+ int entries = 0;
+ struct dirent *dentry = NULL;
+ DIR *dir = NULL;
+ char long_path[PATH_MAX];
+ int srclen;
+ const char *handle_name = NULL;
+
+ ssh_log_hexdump("Processing readdir: handle",
+ (const unsigned char *)ssh_string_get_char(handle),
+ ssh_string_len(handle));
+
+ h = sftp_handle(sftp, client_msg->handle);
+ if (h->type == SFTP_DIR_HANDLE) {
+ dir = h->dirp;
+ handle_name = h->name;
+ }
+ if (dir == NULL) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "got wrong handle from msg");
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+
+ if (handle_name == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+
+ srclen = strlen(handle_name);
+ if (srclen + 2 >= PATH_MAX) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "handle string length exceed max length!");
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+
+ for (int i = 0; i < MAX_ENTRIES_NUM_IN_PACKET; i++) {
+ dentry = readdir(dir);
+
+ if (dentry != NULL) {
+ struct sftp_attributes_struct attr;
+ struct stat st;
+ char long_name[MAX_LONG_NAME_LEN];
+
+ if (strlen(dentry->d_name) + srclen + 1 >= PATH_MAX) {
+ SSH_LOG(SSH_LOG_PROTOCOL,
+ "handle string length exceed max length!");
+ sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
+ return SSH_ERROR;
+ }
+ snprintf(long_path, PATH_MAX, "%s/%s", handle_name, dentry->d_name);
+
+ if (lstat(long_path, &st) == 0) {
+ stat_to_filexfer_attrib(&st, &attr);
+ } else {
+ clear_filexfer_attrib(&attr);
+ }
+
+ if (readdir_long_name(dentry->d_name, &st, long_name) == 0) {
+ sftp_reply_names_add(client_msg, dentry->d_name, long_name, &attr);
+ } else {
+ printf("readdir long name error\n");
+ }
+
+ entries++;
+ } else {
+ break;
+ }
+ }
+
+ if (entries > 0) {
+ ret = sftp_reply_names(client_msg);
+ } else {
+ sftp_reply_status(client_msg, SSH_FX_EOF, NULL);
+ }
+
+ return ret;
+}
+
+static int
+process_mkdir(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ uint32_t msg_flags = client_msg->attr->flags;
+ uint32_t permission = client_msg->attr->permissions;
+ uint32_t mode = (msg_flags & (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS)
+ ? permission & (uint32_t)07777 : 0777;
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing mkdir %s, mode=0%o" PRIu32,
+ filename, mode);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = mkdir(filename, mode);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "failed to mkdir: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ ret = SSH_ERROR;
+ }
+
+ sftp_reply_status(client_msg, status, NULL);
+
+ return ret;
+}
+
+static int
+process_rmdir(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing rmdir %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = rmdir(filename);
+ if (rv < 0) {
+ status = unix_errno_to_ssh_stat(errno);
+ ret = SSH_ERROR;
+ }
+
+ sftp_reply_status(client_msg, status, NULL);
+
+ return ret;
}
-void sftp_handle_remove(sftp_session sftp, void *handle) {
- int i;
+static int
+process_realpath(sftp_client_message client_msg)
+{
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ char *path = NULL;
- for (i = 0; i < SFTP_HANDLES; i++) {
- if (sftp->handles[i] == handle) {
- sftp->handles[i] = NULL;
- break;
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing realpath %s", filename);
+
+ if (filename[0] == '\0') {
+ path = realpath(".", NULL);
+ } else {
+ path = realpath(filename, NULL);
+ }
+ if (path == NULL) {
+ int saved_errno = errno;
+ int status = unix_errno_to_ssh_stat(saved_errno);
+ const char *err_msg = ssh_str_error(status);
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "realpath failed: %s", strerror(saved_errno));
+ sftp_reply_status(client_msg, status, err_msg);
+ return SSH_ERROR;
+ }
+ sftp_reply_name(client_msg, path, NULL);
+ free(path);
+ return SSH_OK;
+}
+
+static int
+process_lstat(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ struct sftp_attributes_struct attr;
+ struct stat st;
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing lstat %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = lstat(filename, &st);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "lstat failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, NULL);
+ ret = SSH_ERROR;
+ } else {
+ stat_to_filexfer_attrib(&st, &attr);
+ sftp_reply_attr(client_msg, &attr);
+ }
+
+ return ret;
+}
+
+static int
+process_stat(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ struct sftp_attributes_struct attr;
+ struct stat st;
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing stat %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = stat(filename, &st);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "lstat failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, NULL);
+ ret = SSH_ERROR;
+ } else {
+ stat_to_filexfer_attrib(&st, &attr);
+ sftp_reply_attr(client_msg, &attr);
+ }
+
+ return ret;
+}
+
+static int
+process_readlink(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ char buf[PATH_MAX];
+ int len = -1;
+ const char *err_msg;
+ int status = SSH_FX_OK;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "Processing readlink %s", filename);
+
+ if (filename == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ len = readlink(filename, buf, sizeof(buf) - 1);
+ if (len < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "readlink failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ err_msg = ssh_str_error(status);
+ sftp_reply_status(client_msg, status, err_msg);
+ ret = SSH_ERROR;
+ } else {
+ buf[len] = '\0';
+ sftp_reply_name(client_msg, buf, NULL);
+ }
+
+ return ret;
+}
+
+/* Note, that this function is using reversed order of the arguments than the
+ * OpenSSH sftp server as they have the arguments switched. See
+ * section "4.1 sftp: Reversal of arguments to SSH_FXP_SYMLINK' in
+ * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
+ * for more information */
+static int
+process_symlink(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *destpath = sftp_client_message_get_filename(client_msg);
+ const char *srcpath = ssh_string_get_char(client_msg->data);
+ int status = SSH_FX_OK;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing symlink: src=%s dest=%s",
+ srcpath, destpath);
+
+ if (srcpath == NULL || destpath == NULL) {
+ sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
+ return SSH_ERROR;
+ }
+
+ rv = symlink(srcpath, destpath);
+ if (rv < 0) {
+ int saved_errno = errno;
+ status = unix_errno_to_ssh_stat(saved_errno);
+ SSH_LOG(SSH_LOG_PROTOCOL, "symlink failed: %s", strerror(saved_errno));
+ sftp_reply_status(client_msg, status, "Write error");
+ ret = SSH_ERROR;
+ } else {
+ sftp_reply_status(client_msg, SSH_FX_OK, "write success");
+ }
+
+ return ret;
+}
+
+static int
+process_remove(sftp_client_message client_msg)
+{
+ int ret = SSH_OK;
+ const char *filename = sftp_client_message_get_filename(client_msg);
+ int rv;
+ int status = SSH_FX_OK;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing remove: %s", filename);
+
+ rv = unlink(filename);
+ if (rv < 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "unlink failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ ret = SSH_ERROR;
+ }
+
+ sftp_reply_status(client_msg, status, NULL);
+
+ return ret;
+}
+
+static int
+process_unsupposed(sftp_client_message client_msg)
+{
+ sftp_reply_status(client_msg, SSH_FX_OP_UNSUPPORTED,
+ "Operation not supported");
+ SSH_LOG(SSH_LOG_PROTOCOL, "Message type %d not implemented",
+ sftp_client_message_get_type(client_msg));
+ return SSH_OK;
+}
+
+static int
+process_extended_statvfs(sftp_client_message client_msg)
+{
+ const char *path = sftp_client_message_get_filename(client_msg);
+ sftp_statvfs_t sftp_statvfs;
+ struct statvfs st;
+ uint64_t flag;
+ int status;
+ int rv;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing extended statvfs: %s", path);
+
+ rv = statvfs(path, &st);
+ if (rv != 0) {
+ int saved_errno = errno;
+ SSH_LOG(SSH_LOG_PROTOCOL, "statvfs failed: %s", strerror(saved_errno));
+ status = unix_errno_to_ssh_stat(saved_errno);
+ sftp_reply_status(client_msg, status, NULL);
+ return SSH_ERROR;
+ }
+
+ sftp_statvfs = calloc(1, sizeof(struct sftp_statvfs_struct));
+ if (sftp_statvfs == NULL) {
+ SSH_LOG(SSH_LOG_PROTOCOL, "Failed to allocate statvfs structure");
+ sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
+ return SSH_ERROR;
+ }
+ flag = (st.f_flag & ST_RDONLY) ? SSH_FXE_STATVFS_ST_RDONLY : 0;
+ flag |= (st.f_flag & ST_NOSUID) ? SSH_FXE_STATVFS_ST_NOSUID : 0;
+
+ sftp_statvfs->f_bsize = st.f_bsize;
+ sftp_statvfs->f_frsize = st.f_frsize;
+ sftp_statvfs->f_blocks = st.f_blocks;
+ sftp_statvfs->f_bfree = st.f_bfree;
+ sftp_statvfs->f_bavail = st.f_bavail;
+ sftp_statvfs->f_files = st.f_files;
+ sftp_statvfs->f_ffree = st.f_ffree;
+ sftp_statvfs->f_favail = st.f_favail;
+ sftp_statvfs->f_fsid = st.f_fsid;
+ sftp_statvfs->f_flag = flag;
+ sftp_statvfs->f_namemax = st.f_namemax;
+
+ rv = sftp_reply_statvfs(client_msg, sftp_statvfs);
+ free(sftp_statvfs);
+ if (rv == 0) {
+ return SSH_OK;
+ }
+ return SSH_ERROR;
+}
+
+static int
+process_extended(sftp_client_message sftp_msg)
+{
+ int status = SSH_ERROR;
+ const char *subtype = sftp_msg->submessage;
+ sftp_server_message_callback handler = NULL;
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing extended message: %s", subtype);
+
+ for (int i = 0; extended_handlers[i].cb != NULL; i++) {
+ if (strcmp(subtype, extended_handlers[i].extended_name) == 0) {
+ handler = extended_handlers[i].cb;
+ break;
+ }
+ }
+ if (handler != NULL) {
+ status = handler(sftp_msg);
+ return status;
+ }
+
+ sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED,
+ "Extended Operation not supported");
+ SSH_LOG(SSH_LOG_PROTOCOL, "Extended Message type %s not implemented",
+ subtype);
+ return SSH_OK;
+}
+
+static int
+dispatch_sftp_request(sftp_client_message sftp_msg)
+{
+ int status = SSH_ERROR;
+ sftp_server_message_callback handler = NULL;
+ uint8_t type = sftp_client_message_get_type(sftp_msg);
+
+ SSH_LOG(SSH_LOG_PROTOCOL, "processing request type: %u", type);
+
+ for (int i = 0; message_handlers[i].cb != NULL; i++) {
+ if (type == message_handlers[i].type) {
+ handler = message_handlers[i].cb;
+ break;
+ }
+ }
+
+ if (handler != NULL) {
+ status = handler(sftp_msg);
+ } else {
+ sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED,
+ "Operation not supported");
+ SSH_LOG(SSH_LOG_PROTOCOL, "Message type %u not implemented", type);
+ return SSH_OK;
+ }
+
+ return status;
+}
+
+static int
+process_client_message(sftp_client_message client_msg)
+{
+ int status = SSH_OK;
+ if (client_msg == NULL) {
+ return SSH_ERROR;
+ }
+
+ switch (client_msg->type) {
+ case SSH_FXP_EXTENDED:
+ status = process_extended(client_msg);
+ break;
+ default:
+ status = dispatch_sftp_request(client_msg);
+ }
+
+ if (status != SSH_OK)
+ SSH_LOG(SSH_LOG_PROTOCOL,
+ "error occurred during processing client message!");
+
+ return status;
+}
+
+/**
+ * @brief Default subsystem request handler for SFTP subsystem
+ *
+ * @param[in] session The ssh session
+ * @param[in] channel The existing ssh channel
+ * @param[in] subsystem The subsystem name. Only "sftp" is handled
+ * @param[out] userdata The pointer to sftp_session which will get the
+ * resulting SFTP session
+ *
+ * @return SSH_OK when the SFTP server was successfully initialized, SSH_ERROR
+ * otherwise.
+ */
+int
+sftp_channel_default_subsystem_request(ssh_session session,
+ ssh_channel channel,
+ const char *subsystem,
+ void *userdata)
+{
+ if (strcmp(subsystem, "sftp") == 0) {
+ sftp_session *sftp = (sftp_session *)userdata;
+
+ /* initialize sftp session and file handler */
+ *sftp = sftp_server_new(session, channel);
+ if (*sftp == NULL) {
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+ }
+ return SSH_ERROR;
+}
+
+/**
+ * @brief Default data callback for sftp server
+ *
+ * @param[in] session The ssh session
+ * @param[in] channel The ssh channel with SFTP session opened
+ * @param[in] data The data to be processed.
+ * @param[in] len The length of input data to be processed
+ * @param[in] is_stderr Unused channel flag for stderr flagging
+ * @param[in] userdata The pointer to sftp_session
+ *
+ * @return number of bytes processed, -1 when error occurs.
+ */
+int
+sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
+ UNUSED_PARAM(ssh_channel channel),
+ void *data,
+ uint32_t len,
+ UNUSED_PARAM(int is_stderr),
+ void *userdata)
+{
+ sftp_session *sftpp = (sftp_session *)userdata;
+ sftp_session sftp = NULL;
+ sftp_client_message msg;
+ int decode_len;
+ int rc;
+
+ if (sftpp == NULL) {
+ SSH_LOG(SSH_LOG_WARNING, "NULL userdata passed to callback");
+ return -1;
}
- }
+ sftp = *sftpp;
+
+ decode_len = sftp_decode_channel_data_to_packet(sftp, data, len);
+ if (decode_len == -1)
+ return -1;
+
+ msg = sftp_get_client_message_from_packet(sftp);
+ rc = process_client_message(msg);
+ sftp_client_message_free(msg);
+ if (rc != SSH_OK)
+ SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
+
+ return decode_len;
}
+#else
+/* Not available on Windows for now */
+int
+sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
+ UNUSED_PARAM(ssh_channel channel),
+ UNUSED_PARAM(void *data),
+ UNUSED_PARAM(uint32_t len),
+ UNUSED_PARAM(int is_stderr),
+ UNUSED_PARAM(void *userdata))
+{
+ return -1;
+}
+
+int
+sftp_channel_default_subsystem_request(UNUSED_PARAM(ssh_session session),
+ UNUSED_PARAM(ssh_channel channel),
+ UNUSED_PARAM(const char *subsystem),
+ UNUSED_PARAM(void *userdata))
+{
+ return SSH_ERROR;
+}
+#endif
diff --git a/src/socket.c b/src/socket.c
index b3594311..9dc4cbdd 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -28,17 +28,15 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
-#if _MSC_VER >= 1400
-#include <io.h>
-#undef open
-#define open _open
-#undef close
-#define close _close
-#undef read
-#define read _read
-#undef write
-#define write _write
-#endif /* _MSC_VER */
+#ifndef UNIX_PATH_MAX
+ /* Inlining the key portions of afunix.h in Windows 10 SDK;
+ * that header isn't available in the mingw environment. */
+#define UNIX_PATH_MAX 108
+struct sockaddr_un {
+ ADDRESS_FAMILY sun_family;
+ char sun_path[UNIX_PATH_MAX];
+};
+#endif
#else /* _WIN32 */
#include <fcntl.h>
#include <sys/types.h>
@@ -56,8 +54,6 @@
#include "libssh/session.h"
/**
- * @internal
- *
* @defgroup libssh_socket The SSH socket functions.
* @ingroup libssh
*
@@ -214,6 +210,15 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
s->callbacks = callbacks;
}
+void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p)
+{
+ s->state = SSH_SOCKET_CONNECTED;
+ /* POLLOUT is the event to wait for in a nonblocking connect */
+ if (p != NULL) {
+ ssh_poll_set_events(p, POLLIN | POLLOUT);
+ }
+}
+
/**
* @brief SSH poll callback. This callback will be used when an event
* caught on the socket.
@@ -221,7 +226,7 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
* @param p Poll object this callback belongs to.
* @param fd The raw socket.
* @param revents The current poll events on the socket.
- * @param userdata Userdata to be passed to the callback function,
+ * @param v_s Userdata to be passed to the callback function,
* in this case the socket object.
*
* @return 0 on success, < 0 when the poll object has been removed
@@ -233,8 +238,8 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
void *v_s)
{
ssh_socket s = (ssh_socket)v_s;
- char buffer[MAX_BUF_SIZE];
- ssize_t nread;
+ void *buffer = NULL;
+ ssize_t nread = 0;
int rc;
int err = 0;
socklen_t errlen = sizeof(err);
@@ -243,7 +248,8 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
if (!ssh_socket_is_open(s)) {
return -1;
}
- SSH_LOG(SSH_LOG_TRACE, "Poll callback on socket %d (%s%s%s), out buffer %d",fd,
+ SSH_LOG(SSH_LOG_TRACE,
+ "Poll callback on socket %d (%s%s%s), out buffer %" PRIu32, fd,
(revents & POLLIN) ? "POLLIN ":"",
(revents & POLLOUT) ? "POLLOUT ":"",
(revents & POLLERR) ? "POLLERR":"",
@@ -275,8 +281,12 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
}
if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) {
s->read_wontblock = 1;
- nread = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer));
+ buffer = ssh_buffer_allocate(s->in_buffer, MAX_BUF_SIZE);
+ if (buffer) {
+ nread = ssh_socket_unbuffered_read(s, buffer, MAX_BUF_SIZE);
+ }
if (nread < 0) {
+ ssh_buffer_pass_bytes_end(s->in_buffer, MAX_BUF_SIZE);
if (p != NULL) {
ssh_poll_remove_events(p, POLLIN);
}
@@ -288,6 +298,10 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
}
return -2;
}
+
+ /* Rollback the unused space */
+ ssh_buffer_pass_bytes_end(s->in_buffer, MAX_BUF_SIZE - nread);
+
if (nread == 0) {
if (p != NULL) {
ssh_poll_remove_events(p, POLLIN);
@@ -304,18 +318,15 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
s->session->socket_counter->in_bytes += nread;
}
- /* Bufferize the data and then call the callback */
- rc = ssh_buffer_add_data(s->in_buffer, buffer, nread);
- if (rc < 0) {
- return -1;
- }
+ /* Call the callback */
if (s->callbacks != NULL && s->callbacks->data != NULL) {
+ size_t processed;
do {
- nread = s->callbacks->data(ssh_buffer_get(s->in_buffer),
- ssh_buffer_get_len(s->in_buffer),
- s->callbacks->userdata);
- ssh_buffer_pass_bytes(s->in_buffer, nread);
- } while ((nread > 0) && (s->state == SSH_SOCKET_CONNECTED));
+ processed = s->callbacks->data(ssh_buffer_get(s->in_buffer),
+ ssh_buffer_get_len(s->in_buffer),
+ s->callbacks->userdata);
+ ssh_buffer_pass_bytes(s->in_buffer, processed);
+ } while ((processed > 0) && (s->state == SSH_SOCKET_CONNECTED));
/* p may have been freed, so don't use it
* anymore in this function */
@@ -332,10 +343,7 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
/* First, POLLOUT is a sign we may be connected */
if (s->state == SSH_SOCKET_CONNECTING) {
SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state");
- s->state = SSH_SOCKET_CONNECTED;
- if (p != NULL) {
- ssh_poll_set_events(p, POLLOUT | POLLIN);
- }
+ ssh_socket_set_connected(s, p);
rc = ssh_socket_set_blocking(ssh_socket_get_fd(s));
if (rc < 0) {
@@ -363,7 +371,7 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
ssh_socket_nonblocking_flush(s);
} else if (s->callbacks != NULL && s->callbacks->controlflow != NULL) {
/* Otherwise advertise the upper level that write can be done */
- SSH_LOG(SSH_LOG_TRACE,"sending control flow event");
+ SSH_LOG(SSH_LOG_TRACE, "sending control flow event");
s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,
s->callbacks->userdata);
}
@@ -388,7 +396,7 @@ ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
if (s->poll_handle) {
return s->poll_handle;
}
- s->poll_handle = ssh_poll_new(s->fd,0,ssh_socket_pollcallback,s);
+ s->poll_handle = ssh_poll_new(s->fd, 0, ssh_socket_pollcallback, s);
return s->poll_handle;
}
@@ -406,10 +414,10 @@ void ssh_socket_free(ssh_socket s)
SAFE_FREE(s);
}
-#ifndef _WIN32
int ssh_socket_unix(ssh_socket s, const char *path)
{
struct sockaddr_un sunaddr;
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
socket_t fd;
sunaddr.sun_family = AF_UNIX;
snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
@@ -418,28 +426,30 @@ int ssh_socket_unix(ssh_socket s, const char *path)
if (fd == SSH_INVALID_SOCKET) {
ssh_set_error(s->session, SSH_FATAL,
"Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
- strerror(errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return -1;
}
+#ifndef _WIN32
if (fcntl(fd, F_SETFD, 1) == -1) {
ssh_set_error(s->session, SSH_FATAL,
"Error from fcntl(fd, F_SETFD, 1): %s",
- strerror(errno));
- close(fd);
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
return -1;
}
+#endif
if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) {
- ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
- strerror(errno));
- close(fd);
+ ssh_set_error(s->session, SSH_FATAL, "Error from connect(%s): %s",
+ path,
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ CLOSE_SOCKET(fd);
return -1;
}
ssh_socket_set_fd(s,fd);
return 0;
}
-#endif
/** \internal
* \brief closes a socket
@@ -473,12 +483,14 @@ void ssh_socket_close(ssh_socket s)
kill(pid, SIGTERM);
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
- SSH_LOG(SSH_LOG_WARN, "waitpid failed: %s", strerror(errno));
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+ SSH_LOG(SSH_LOG_TRACE, "waitpid failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return;
}
}
if (!WIFEXITED(status)) {
- SSH_LOG(SSH_LOG_WARN, "Proxy command exitted abnormally");
+ SSH_LOG(SSH_LOG_TRACE, "Proxy command exited abnormally");
return;
}
SSH_LOG(SSH_LOG_TRACE, "Proxy command returned %d", WEXITSTATUS(status));
@@ -491,22 +503,28 @@ void ssh_socket_close(ssh_socket s)
* @brief sets the file descriptor of the socket.
* @param[out] s ssh_socket to update
* @param[in] fd file descriptor to set
- * @warning this function updates boths the input and output
+ * @warning this function updates both the input and output
* file descriptors
*/
void ssh_socket_set_fd(ssh_socket s, socket_t fd)
{
+ ssh_poll_handle h = NULL;
+
s->fd = fd;
if (s->poll_handle) {
ssh_poll_set_fd(s->poll_handle,fd);
} else {
s->state = SSH_SOCKET_CONNECTING;
+ h = ssh_socket_get_poll_handle(s);
+ if (h == NULL) {
+ return;
+ }
/* POLLOUT is the event to wait for in a nonblocking connect */
- ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLOUT);
+ ssh_poll_set_events(h, POLLOUT);
#ifdef _WIN32
- ssh_poll_add_events(ssh_socket_get_poll_handle(s), POLLWRNORM);
+ ssh_poll_add_events(h, POLLWRNORM);
#endif
}
}
@@ -540,9 +558,9 @@ static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
return -1;
}
if (s->fd_is_socket) {
- rc = recv(s->fd,buffer, len, 0);
+ rc = recv(s->fd, buffer, len, 0);
} else {
- rc = read(s->fd,buffer, len);
+ rc = read(s->fd, buffer, len);
}
#ifdef _WIN32
s->last_errno = WSAGetLastError();
@@ -553,6 +571,8 @@ static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
if (rc < 0) {
s->data_except = 1;
+ } else {
+ SSH_LOG(SSH_LOG_TRACE, "read %zd", rc);
}
return rc;
@@ -590,12 +610,13 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
/* Reactive the POLLOUT detector in the poll multiplexer system */
if (s->poll_handle) {
SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket");
- ssh_poll_set_events(s->poll_handle,ssh_poll_get_events(s->poll_handle) | POLLOUT);
+ ssh_poll_add_events(s->poll_handle, POLLOUT);
}
if (w < 0) {
s->data_except = 1;
}
+ SSH_LOG(SSH_LOG_TRACE, "wrote %zd", w);
return w;
}
@@ -634,7 +655,7 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
* \returns SSH_OK, or SSH_ERROR
* \warning has no effect on socket before a flush
*/
-int ssh_socket_write(ssh_socket s, const void *buffer, int len)
+int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
{
if (len > 0) {
if (ssh_buffer_add_data(s->out_buffer, buffer, len) < 0) {
@@ -664,11 +685,12 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
s->last_errno,
s->callbacks->userdata);
} else {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(session,
SSH_FATAL,
"Writing packet: error on socket (or connection "
"closed): %s",
- strerror(s->last_errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
}
return SSH_ERROR;
@@ -697,11 +719,12 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
s->last_errno,
s->callbacks->userdata);
} else {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(session,
SSH_FATAL,
"Writing packet: error on socket (or connection "
"closed): %s",
- strerror(s->last_errno));
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
}
return SSH_ERROR;
@@ -716,6 +739,8 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
/* Is there some data pending? */
len = ssh_buffer_get_len(s->out_buffer);
if (s->poll_handle && len > 0) {
+ SSH_LOG(SSH_LOG_TRACE,
+ "did not send all the data, queuing pollout event");
/* force the poll system to catch pollout events */
ssh_poll_add_events(s->poll_handle, POLLOUT);
@@ -825,7 +850,7 @@ int ssh_socket_set_blocking(socket_t fd)
/**
* @internal
* @brief Launches a socket connection
- * If a the socket connected callback has been defined and
+ * If the socket connected callback has been defined and
* a poll object exists, this call will be non blocking.
* @param s socket to connect.
* @param host hostname or ip address to connect to.
@@ -833,8 +858,6 @@ int ssh_socket_set_blocking(socket_t fd)
* @param bind_addr address to bind to, or NULL for default.
* @returns SSH_OK socket is being connected.
* @returns SSH_ERROR error while connecting to remote host.
- * @bug It only tries connecting to one of the available AI's
- * which is problematic for hosts having DNS fail-over.
*/
int ssh_socket_connect(ssh_socket s,
const char *host,
@@ -842,19 +865,19 @@ int ssh_socket_connect(ssh_socket s,
const char *bind_addr)
{
socket_t fd;
-
+
if (s->state != SSH_SOCKET_NONE) {
ssh_set_error(s->session, SSH_FATAL,
"ssh_socket_connect called on socket not unconnected");
return SSH_ERROR;
}
fd = ssh_connect_host_nonblocking(s->session, host, bind_addr, port);
- SSH_LOG(SSH_LOG_PROTOCOL, "Nonblocking connection socket: %d", fd);
+ SSH_LOG(SSH_LOG_DEBUG, "Nonblocking connection socket: %d", fd);
if (fd == SSH_INVALID_SOCKET) {
return SSH_ERROR;
}
ssh_socket_set_fd(s,fd);
-
+
return SSH_OK;
}
@@ -872,20 +895,29 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
const char *shell = NULL;
const char *args[] = {NULL/*shell*/, "-c", command, NULL};
int devnull;
+ int rc;
/* Prepare /dev/null socket for the stderr redirection */
devnull = open("/dev/null", O_WRONLY);
if (devnull == -1) {
- SSH_LOG(SSH_LOG_WARNING, "Failed to open /dev/null");
+ SSH_LOG(SSH_LOG_TRACE, "Failed to open /dev/null");
exit(1);
}
- /* By default, use the current users shell */
+ /*
+ * By default, use the current users shell. This could fail with some
+ * shells like zsh or dash ...
+ */
shell = getenv("SHELL");
if (shell == NULL || shell[0] == '\0') {
- /* Fall back to bash. There are issues with dash or
- * whatever people tend to link to /bin/sh */
- shell = "/bin/bash";
+ /* Fall back to the /bin/sh only if the bash is not available. But there are
+ * issues with dash or whatever people tend to link to /bin/sh */
+ rc = access("/bin/bash", 0);
+ if (rc != 0) {
+ shell = "/bin/sh";
+ } else {
+ shell = "/bin/bash";
+ }
}
args[0] = shell;
@@ -896,7 +928,13 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
dup2(devnull, STDERR_FILENO);
close(in);
close(out);
- execv(args[0], (char * const *)args);
+ rc = execv(args[0], (char * const *)args);
+ if (rc < 0) {
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
+
+ SSH_LOG(SSH_LOG_WARN, "Failed to execute command %s: %s",
+ command, ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ }
exit(1);
}
@@ -914,6 +952,7 @@ int
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
{
socket_t pair[2];
+ ssh_poll_handle h = NULL;
int pid;
int rc;
@@ -926,7 +965,7 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
return SSH_ERROR;
}
- SSH_LOG(SSH_LOG_PROTOCOL, "Executing proxycommand '%s'", command);
+ SSH_LOG(SSH_LOG_DEBUG, "Executing proxycommand '%s'", command);
pid = fork();
if (pid == 0) {
ssh_execute_command(command, pair[0], pair[0]);
@@ -934,12 +973,14 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
}
s->proxy_pid = pid;
close(pair[0]);
- SSH_LOG(SSH_LOG_PROTOCOL, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
+ SSH_LOG(SSH_LOG_DEBUG, "ProxyCommand connection pipe: [%d,%d]",pair[0],pair[1]);
ssh_socket_set_fd(s, pair[1]);
- s->state=SSH_SOCKET_CONNECTED;
- s->fd_is_socket=0;
- /* POLLOUT is the event to wait for in a nonblocking connect */
- ssh_poll_set_events(ssh_socket_get_poll_handle(s), POLLIN | POLLOUT);
+ s->fd_is_socket = 0;
+ h = ssh_socket_get_poll_handle(s);
+ if (h == NULL) {
+ return SSH_ERROR;
+ }
+ ssh_socket_set_connected(s, h);
if (s->callbacks && s->callbacks->connected) {
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata);
}
diff --git a/src/string.c b/src/string.c
index acd3cf48..44403487 100644
--- a/src/string.c
+++ b/src/string.c
@@ -103,7 +103,7 @@ int ssh_string_fill(struct ssh_string_struct *s, const void *data, size_t len) {
* @return The newly allocated string, NULL on error with errno
* set.
*
- * @note The nul byte is not copied nor counted in the ouput string.
+ * @note The null byte is not copied nor counted in the output string.
*/
struct ssh_string_struct *ssh_string_from_char(const char *what) {
struct ssh_string_struct *ptr;
@@ -129,7 +129,7 @@ struct ssh_string_struct *ssh_string_from_char(const char *what) {
/**
* @brief Return the size of a SSH string.
*
- * @param[in] s The the input SSH string.
+ * @param[in] s The input SSH string.
*
* @return The size of the content of the string, 0 on error.
*/
@@ -149,7 +149,7 @@ size_t ssh_string_len(struct ssh_string_struct *s) {
}
/**
- * @brief Get the the string as a C nul-terminated string.
+ * @brief Get the string as a C null-terminated string.
*
* This is only available as long as the SSH string exists.
*
@@ -168,7 +168,7 @@ const char *ssh_string_get_char(struct ssh_string_struct *s)
}
/**
- * @brief Convert a SSH string to a C nul-terminated string.
+ * @brief Convert a SSH string to a C null-terminated string.
*
* @param[in] s The SSH input string.
*
diff --git a/src/threads.c b/src/threads.c
index 792b976d..7660b972 100644
--- a/src/threads.c
+++ b/src/threads.c
@@ -20,7 +20,7 @@
*/
/**
- * @defgroup libssh_threads The SSH threading functions.
+ * @defgroup libssh_threads The SSH threading functions
* @ingroup libssh
*
* Threading with libssh
diff --git a/src/threads/libcrypto.c b/src/threads/libcrypto.c
index 5786948b..18951b6a 100644
--- a/src/threads/libcrypto.c
+++ b/src/threads/libcrypto.c
@@ -24,8 +24,6 @@
#include "libssh/threads.h"
#include <libssh/callbacks.h>
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000)
-
int crypto_thread_init(struct ssh_threads_callbacks_struct *cb)
{
(void) cb;
@@ -36,98 +34,3 @@ void crypto_thread_finalize(void)
{
return;
}
-
-#else
-
-static struct ssh_threads_callbacks_struct *user_callbacks = NULL;
-
-static void **libcrypto_mutexes;
-
-void libcrypto_lock_callback(int mode, int i, const char *file, int line);
-
-void libcrypto_lock_callback(int mode, int i, const char *file, int line)
-{
- (void)file;
- (void)line;
-
- if (mode & CRYPTO_LOCK) {
- user_callbacks->mutex_lock(&libcrypto_mutexes[i]);
- } else {
- user_callbacks->mutex_unlock(&libcrypto_mutexes[i]);
- }
-}
-
-#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK
-static void libcrypto_THREADID_callback(CRYPTO_THREADID *id)
-{
- unsigned long thread_id = (*user_callbacks->thread_id)();
-
- CRYPTO_THREADID_set_numeric(id, thread_id);
-}
-#endif /* HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK */
-
-int crypto_thread_init(struct ssh_threads_callbacks_struct *cb)
-{
- int n = CRYPTO_num_locks();
- int cmp;
- int i;
-
- if (cb == NULL) {
- return SSH_OK;
- }
-
- if (user_callbacks != NULL) {
- crypto_thread_finalize();
- }
-
- user_callbacks = cb;
-
- cmp = strcmp(user_callbacks->type, "threads_noop");
- if (cmp == 0) {
- return SSH_OK;
- }
-
- libcrypto_mutexes = calloc(n, sizeof(void *));
- if (libcrypto_mutexes == NULL) {
- return SSH_ERROR;
- }
-
- for (i = 0; i < n; ++i){
- user_callbacks->mutex_init(&libcrypto_mutexes[i]);
- }
-
-#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK
- CRYPTO_THREADID_set_callback(libcrypto_THREADID_callback);
-#else
- CRYPTO_set_id_callback(user_callbacks->thread_id);
-#endif
-
- CRYPTO_set_locking_callback(libcrypto_lock_callback);
-
- return SSH_OK;
-}
-
-void crypto_thread_finalize(void)
-{
- int n = CRYPTO_num_locks();
- int i;
-
- if (libcrypto_mutexes == NULL) {
- return;
- }
-
-#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK
- CRYPTO_THREADID_set_callback(NULL);
-#else
- CRYPTO_set_id_callback(NULL);
-#endif
-
- CRYPTO_set_locking_callback(NULL);
-
- for (i = 0; i < n; ++i) {
- user_callbacks->mutex_destroy(&libcrypto_mutexes[i]);
- }
- SAFE_FREE(libcrypto_mutexes);
-}
-
-#endif
diff --git a/src/threads/winlocks.c b/src/threads/winlocks.c
index a1799531..da600418 100644
--- a/src/threads/winlocks.c
+++ b/src/threads/winlocks.c
@@ -26,6 +26,7 @@
#include <windows.h>
#include <winbase.h>
#include <errno.h>
+#include <stdlib.h>
static int ssh_winlock_mutex_init (void **priv)
{
diff --git a/src/token.c b/src/token.c
index 0924d3bd..6ce2b64c 100644
--- a/src/token.c
+++ b/src/token.c
@@ -87,7 +87,7 @@ struct ssh_tokens_st *ssh_tokenize(const char *chain, char separator)
return NULL;
}
- tokens->buffer= strdup(chain);
+ tokens->buffer = strdup(chain);
if (tokens->buffer == NULL) {
goto error;
}
@@ -376,6 +376,7 @@ char *ssh_append_without_duplicates(const char *list,
{
size_t concat_len = 0;
char *ret = NULL, *concat = NULL;
+ int rc = 0;
if (list != NULL) {
concat_len = strlen(list);
@@ -396,12 +397,144 @@ char *ssh_append_without_duplicates(const char *list,
return NULL;
}
+ rc = snprintf(concat, concat_len, "%s%s%s",
+ list == NULL ? "" : list,
+ list == NULL ? "" : ",",
+ appended_list == NULL ? "" : appended_list);
+ if (rc < 0) {
+ SAFE_FREE(concat);
+ return NULL;
+ }
+
+ ret = ssh_remove_duplicates(concat);
+
+ SAFE_FREE(concat);
+
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Given two strings containing lists of tokens, return a newly
+ * allocated string containing the elements of the first list without the
+ * elements of the second list. The order of the elements will be preserved.
+ *
+ * @param[in] list The first list
+ * @param[in] remove_list The list to be removed
+ *
+ * @return A newly allocated copy list containing elements of the
+ * list without the elements of remove_list; NULL in case of error.
+ */
+char *ssh_remove_all_matching(const char *list,
+ const char *remove_list)
+{
+ struct ssh_tokens_st *l_tok = NULL, *r_tok = NULL;
+ int i, j, cmp;
+ char *ret = NULL;
+ size_t len, pos = 0;
+ bool exclude;
+
+ if (list == NULL) {
+ return NULL;
+ }
+ if (remove_list == NULL) {
+ return strdup (list);
+ }
+
+ l_tok = ssh_tokenize(list, ',');
+ if (l_tok == NULL) {
+ goto out;
+ }
+
+ r_tok = ssh_tokenize(remove_list, ',');
+ if (r_tok == NULL) {
+ goto out;
+ }
+
+ ret = calloc(1, strlen(list) + 1);
+ if (ret == NULL) {
+ goto out;
+ }
+
+ for (i = 0; l_tok->tokens[i]; i++) {
+ exclude = false;
+ for (j = 0; r_tok->tokens[j]; j++) {
+ cmp = strcmp(l_tok->tokens[i], r_tok->tokens[j]);
+ if (cmp == 0) {
+ exclude = true;
+ break;
+ }
+ }
+ if (exclude == false) {
+ if (pos != 0) {
+ ret[pos] = ',';
+ pos++;
+ }
+
+ len = strlen(l_tok->tokens[i]);
+ memcpy(&ret[pos], l_tok->tokens[i], len);
+ pos += len;
+ }
+ }
+
+ if (ret[0] == '\0') {
+ SAFE_FREE(ret);
+ }
+
+out:
+ ssh_tokens_free(l_tok);
+ ssh_tokens_free(r_tok);
+ return ret;
+}
+
+/**
+ * @internal
+ *
+ * @brief Given two strings containing lists of tokens, return a newly
+ * allocated string containing all the elements of the first list prefixed at
+ * the beginning of the second list, without duplicates.
+ *
+ * @param[in] list The first list
+ * @param[in] prefixed_list The list to use as a prefix
+ *
+ * @return A newly allocated list containing all the elements
+ * of the list prefixed with the elements of the prefixed_list without
+ * duplicates; NULL in case of error.
+ */
+char *ssh_prefix_without_duplicates(const char *list,
+ const char *prefixed_list)
+{
+ size_t concat_len = 0;
+ char *ret = NULL, *concat = NULL;
+ int rc = 0;
+
if (list != NULL) {
- strcpy(concat, list);
- strncat(concat, ",", concat_len - strlen(concat) - 1);
+ concat_len = strlen(list);
}
- if (appended_list != NULL) {
- strncat(concat, appended_list, concat_len - strlen(concat) - 1);
+
+ if (prefixed_list != NULL) {
+ concat_len += strlen(prefixed_list);
+ }
+
+ if (concat_len == 0) {
+ return NULL;
+ }
+
+ /* Add room for ending '\0' and for middle ',' */
+ concat_len += 2;
+ concat = calloc(concat_len, 1);
+ if (concat == NULL) {
+ return NULL;
+ }
+
+ rc = snprintf(concat, concat_len, "%s%s%s",
+ prefixed_list == NULL ? "" : prefixed_list,
+ prefixed_list == NULL ? "" : ",",
+ list == NULL ? "" : list);
+ if (rc < 0) {
+ SAFE_FREE(concat);
+ return NULL;
}
ret = ssh_remove_duplicates(concat);
diff --git a/src/ttyopts.c b/src/ttyopts.c
new file mode 100644
index 00000000..f6557884
--- /dev/null
+++ b/src/ttyopts.c
@@ -0,0 +1,449 @@
+/*
+ * ttyopts.c - encoding of TTY modes.
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2023 by Utimaco TS GmbH <oss_committee@utimaco.com>
+ * Author: Daniel Evers <daniel.evers@utimaco.com>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <libssh/priv.h>
+#include <string.h>
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+/** Terminal mode opcodes */
+enum {
+ TTY_OP_END = 0,
+ TTY_OP_VINTR = 1,
+ TTY_OP_VQUIT = 2,
+ TTY_OP_VERASE = 3,
+ TTY_OP_VKILL = 4,
+ TTY_OP_VEOF = 5,
+ TTY_OP_VEOL = 6,
+ TTY_OP_VEOL2 = 7,
+ TTY_OP_VSTART = 8,
+ TTY_OP_VSTOP = 9,
+ TTY_OP_VSUSP = 10,
+ TTY_OP_VDSUSP = 11,
+ TTY_OP_VREPRINT = 12,
+ TTY_OP_VWERASE = 13,
+ TTY_OP_VLNEXT = 14,
+ TTY_OP_VFLUSH = 15,
+ TTY_OP_VSWTC = 16,
+ TTY_OP_VSTATUS = 17,
+ TTY_OP_VDISCARD = 18,
+ TTY_OP_IGNPAR = 30,
+ TTY_OP_PARMRK = 31,
+ TTY_OP_INPCK = 32,
+ TTY_OP_ISTRIP = 33,
+ TTY_OP_INLCR = 34,
+ TTY_OP_IGNCR = 35,
+ TTY_OP_ICRNL = 36,
+ TTY_OP_IUCLC = 37,
+ TTY_OP_IXON = 38,
+ TTY_OP_IXANY = 39,
+ TTY_OP_IXOFF = 40,
+ TTY_OP_IMAXBEL = 41,
+ TTY_OP_IUTF8 = 42,
+ TTY_OP_ISIG = 50,
+ TTY_OP_ICANON = 51,
+ TTY_OP_XCASE = 52,
+ TTY_OP_ECHO = 53,
+ TTY_OP_ECHOE = 54,
+ TTY_OP_ECHOK = 55,
+ TTY_OP_ECHONL = 56,
+ TTY_OP_NOFLSH = 57,
+ TTY_OP_TOSTOP = 58,
+ TTY_OP_IEXTEN = 59,
+ TTY_OP_ECHOCTL = 60,
+ TTY_OP_ECHOKE = 61,
+ TTY_OP_PENDIN = 62,
+ TTY_OP_OPOST = 70,
+ TTY_OP_OLCUC = 71,
+ TTY_OP_ONLCR = 72,
+ TTY_OP_OCRNL = 73,
+ TTY_OP_ONOCR = 74,
+ TTY_OP_ONLRET = 75,
+ TTY_OP_CS7 = 90,
+ TTY_OP_CS8 = 91,
+ TTY_OP_PARENB = 92,
+ TTY_OP_PARODD = 93,
+ TTY_OP_ISPEED = 128,
+ TTY_OP_OSPEED = 129,
+};
+
+/**
+ * Encodes a single SSH terminal mode option into the buffer.
+ *
+ * @param[in] attr The mode's opcode value.
+ *
+ * @param[in] value The mode's value.
+ *
+ * @param[out] buf Destination buffer to encode into.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes written to the buffer on success, -1 on
+ * error.
+ */
+static int
+encode_termios_opt(unsigned char opcode,
+ uint32_t value,
+ unsigned char *buf,
+ size_t buflen)
+{
+ int offset = 0;
+
+ /* always need 5 bytes */
+ if (buflen < 5) {
+ return -1;
+ }
+
+ /* 1 byte opcode */
+ buf[offset++] = opcode;
+
+ /* 4 bytes value (big endian) */
+ value = htonl(value);
+ memcpy(buf + offset, &value, sizeof(value));
+ offset += sizeof(value);
+
+ return offset;
+}
+
+#ifdef HAVE_TERMIOS_H
+/** Converts a baudrate constant (Bxxxx) to a numeric value. */
+static int
+baud2speed(int baudrate)
+{
+ switch (baudrate) {
+ default:
+ case B0:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+ case B19200:
+ return 19200;
+ case B38400:
+ return 38400;
+ case B57600:
+ return 57600;
+ case B115200:
+ return 115200;
+ case B230400:
+ return 230400;
+ }
+}
+
+/**
+ * Encodes all terminal options from the given \c termios structure
+ * into the buffer.
+ *
+ * @param[in] attr The terminal options to encode.
+ *
+ * @param[out] buf Modes will be encoded into this buffer.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes in the buffer on success, -1 on error.
+ */
+static int
+encode_termios_opts(struct termios *attr, unsigned char *buf, size_t buflen)
+{
+ unsigned int offset = 0;
+ int rc;
+
+#define SSH_ENCODE_OPT(code, value) \
+ rc = encode_termios_opt(code, value, buf + offset, buflen - offset); \
+ if (rc < 0) { \
+ return rc; \
+ } else { \
+ offset += rc; \
+ }
+
+#define SSH_ENCODE_INPUT_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_iflag & opt) ? 1 : 0)
+ SSH_ENCODE_INPUT_OPT(IGNPAR)
+ SSH_ENCODE_INPUT_OPT(PARMRK)
+ SSH_ENCODE_INPUT_OPT(INPCK)
+ SSH_ENCODE_INPUT_OPT(ISTRIP)
+ SSH_ENCODE_INPUT_OPT(INLCR)
+ SSH_ENCODE_INPUT_OPT(IGNCR)
+ SSH_ENCODE_INPUT_OPT(ICRNL)
+#ifdef IUCLC
+ SSH_ENCODE_INPUT_OPT(IUCLC)
+#endif
+ SSH_ENCODE_INPUT_OPT(IXON)
+ SSH_ENCODE_INPUT_OPT(IXANY)
+ SSH_ENCODE_INPUT_OPT(IXOFF)
+ SSH_ENCODE_INPUT_OPT(IMAXBEL)
+#ifdef IUTF8
+ SSH_ENCODE_INPUT_OPT(IUTF8)
+#endif
+#undef SSH_ENCODE_INPUT_OPT
+
+#define SSH_ENCODE_OUTPUT_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_oflag & opt) ? 1 : 0)
+ SSH_ENCODE_OUTPUT_OPT(OPOST)
+#ifdef OLCUC
+ SSH_ENCODE_OUTPUT_OPT(OLCUC)
+#endif
+ SSH_ENCODE_OUTPUT_OPT(ONLCR)
+ SSH_ENCODE_OUTPUT_OPT(OCRNL)
+ SSH_ENCODE_OUTPUT_OPT(ONOCR)
+ SSH_ENCODE_OUTPUT_OPT(ONLRET)
+#undef SSH_ENCODE_OUTPUT_OPT
+
+#define SSH_ENCODE_CONTROL_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_cflag & opt) ? 1 : 0)
+ SSH_ENCODE_CONTROL_OPT(CS7)
+ SSH_ENCODE_CONTROL_OPT(CS8)
+ SSH_ENCODE_CONTROL_OPT(PARENB)
+ SSH_ENCODE_CONTROL_OPT(PARODD)
+#undef SSH_ENCODE_CONTROL_OPT
+
+#define SSH_ENCODE_LOCAL_OPT(opt) \
+ SSH_ENCODE_OPT(TTY_OP_##opt, (attr->c_lflag & opt) ? 1 : 0)
+ SSH_ENCODE_LOCAL_OPT(ISIG)
+ SSH_ENCODE_LOCAL_OPT(ICANON)
+#ifdef XCASE
+ SSH_ENCODE_LOCAL_OPT(XCASE)
+#endif
+ SSH_ENCODE_LOCAL_OPT(ECHO)
+ SSH_ENCODE_LOCAL_OPT(ECHOE)
+ SSH_ENCODE_LOCAL_OPT(ECHOK)
+ SSH_ENCODE_LOCAL_OPT(ECHONL)
+ SSH_ENCODE_LOCAL_OPT(NOFLSH)
+ SSH_ENCODE_LOCAL_OPT(TOSTOP)
+ SSH_ENCODE_LOCAL_OPT(IEXTEN)
+ SSH_ENCODE_LOCAL_OPT(ECHOCTL)
+ SSH_ENCODE_LOCAL_OPT(ECHOKE)
+ SSH_ENCODE_LOCAL_OPT(PENDIN)
+#undef SSH_ENCODE_LOCAL_OPT
+
+#define SSH_ENCODE_CC_OPT(opt) SSH_ENCODE_OPT(TTY_OP_##opt, attr->c_cc[opt])
+ SSH_ENCODE_CC_OPT(VINTR)
+ SSH_ENCODE_CC_OPT(VQUIT)
+ SSH_ENCODE_CC_OPT(VERASE)
+ SSH_ENCODE_CC_OPT(VKILL)
+ SSH_ENCODE_CC_OPT(VEOF)
+ SSH_ENCODE_CC_OPT(VEOL)
+ SSH_ENCODE_CC_OPT(VEOL2)
+ SSH_ENCODE_CC_OPT(VSTART)
+ SSH_ENCODE_CC_OPT(VSTOP)
+ SSH_ENCODE_CC_OPT(VSUSP)
+#ifdef VDSUSP
+ SSH_ENCODE_CC_OPT(VDSUSP)
+#endif
+ SSH_ENCODE_CC_OPT(VREPRINT)
+ SSH_ENCODE_CC_OPT(VWERASE)
+ SSH_ENCODE_CC_OPT(VLNEXT)
+#ifdef VFLUSH
+ SSH_ENCODE_CC_OPT(VFLUSH)
+#endif
+#ifdef VSWTC
+ SSH_ENCODE_CC_OPT(VSWTC)
+#endif
+#ifdef VSTATUS
+ SSH_ENCODE_CC_OPT(VSTATUS)
+#endif
+ SSH_ENCODE_CC_OPT(VDISCARD)
+#undef SSH_ENCODE_CC_OPT
+
+ SSH_ENCODE_OPT(TTY_OP_ISPEED, baud2speed(cfgetispeed(attr)))
+ SSH_ENCODE_OPT(TTY_OP_OSPEED, baud2speed(cfgetospeed(attr)))
+#undef SSH_ENCODE_OPT
+
+ /* end of options */
+ if (buflen > offset) {
+ buf[offset++] = TTY_OP_END;
+ } else {
+ return -1;
+ }
+
+ return (int)offset;
+}
+#endif
+
+/**
+ * Encodes a set of default options to ensure "sane" PTY behavior.
+ * This function intentionally doesn't use the \c termios structure
+ * to allow it to work on Windows as well.
+ *
+ * @param[out] buf Modes will be encoded into this buffer.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes in the buffer on success, -1 on error.
+ */
+static int
+encode_default_opts(unsigned char *buf, size_t buflen)
+{
+ unsigned int offset = 0;
+ int rc;
+
+#define SSH_ENCODE_OPT(code, value) \
+ rc = encode_termios_opt(code, value, buf + offset, buflen - offset); \
+ if (rc < 0) { \
+ return rc; \
+ } else { \
+ offset += rc; \
+ }
+
+ SSH_ENCODE_OPT(TTY_OP_VINTR, 003)
+ SSH_ENCODE_OPT(TTY_OP_VQUIT, 034)
+ SSH_ENCODE_OPT(TTY_OP_VERASE, 0177)
+ SSH_ENCODE_OPT(TTY_OP_VKILL, 025)
+ SSH_ENCODE_OPT(TTY_OP_VEOF, 0)
+ SSH_ENCODE_OPT(TTY_OP_VEOL, 0)
+ SSH_ENCODE_OPT(TTY_OP_VEOL2, 0)
+ SSH_ENCODE_OPT(TTY_OP_VSTART, 021)
+ SSH_ENCODE_OPT(TTY_OP_VSTOP, 023)
+ SSH_ENCODE_OPT(TTY_OP_VSUSP, 032)
+ SSH_ENCODE_OPT(TTY_OP_VDSUSP, 031)
+ SSH_ENCODE_OPT(TTY_OP_VREPRINT, 022)
+ SSH_ENCODE_OPT(TTY_OP_VWERASE, 027)
+ SSH_ENCODE_OPT(TTY_OP_VLNEXT, 026)
+ SSH_ENCODE_OPT(TTY_OP_VDISCARD, 017)
+ SSH_ENCODE_OPT(TTY_OP_IGNPAR, 0)
+ SSH_ENCODE_OPT(TTY_OP_PARMRK, 0)
+ SSH_ENCODE_OPT(TTY_OP_INPCK, 0)
+ SSH_ENCODE_OPT(TTY_OP_ISTRIP, 0)
+ SSH_ENCODE_OPT(TTY_OP_INLCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_IGNCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_ICRNL, 0)
+ SSH_ENCODE_OPT(TTY_OP_IUCLC, 0)
+ SSH_ENCODE_OPT(TTY_OP_IXON, 1)
+ SSH_ENCODE_OPT(TTY_OP_IXANY, 0)
+ SSH_ENCODE_OPT(TTY_OP_IXOFF, 0)
+ SSH_ENCODE_OPT(TTY_OP_IMAXBEL, 0)
+ SSH_ENCODE_OPT(TTY_OP_IUTF8, 1)
+ SSH_ENCODE_OPT(TTY_OP_ISIG, 1)
+ SSH_ENCODE_OPT(TTY_OP_ICANON, 1)
+ SSH_ENCODE_OPT(TTY_OP_XCASE, 0)
+ SSH_ENCODE_OPT(TTY_OP_ECHO, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHOE, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHOK, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHONL, 0)
+ SSH_ENCODE_OPT(TTY_OP_NOFLSH, 0)
+ SSH_ENCODE_OPT(TTY_OP_TOSTOP, 0)
+ SSH_ENCODE_OPT(TTY_OP_IEXTEN, 1)
+ SSH_ENCODE_OPT(TTY_OP_ECHOCTL, 0)
+ SSH_ENCODE_OPT(TTY_OP_ECHOKE, 1)
+ SSH_ENCODE_OPT(TTY_OP_PENDIN, 0)
+ SSH_ENCODE_OPT(TTY_OP_OPOST, 1)
+ SSH_ENCODE_OPT(TTY_OP_OLCUC, 0)
+ SSH_ENCODE_OPT(TTY_OP_ONLCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_OCRNL, 0)
+ SSH_ENCODE_OPT(TTY_OP_ONOCR, 0)
+ SSH_ENCODE_OPT(TTY_OP_ONLRET, 0)
+ SSH_ENCODE_OPT(TTY_OP_CS7, 1)
+ SSH_ENCODE_OPT(TTY_OP_CS8, 1)
+ SSH_ENCODE_OPT(TTY_OP_PARENB, 0)
+ SSH_ENCODE_OPT(TTY_OP_PARODD, 0)
+ SSH_ENCODE_OPT(TTY_OP_ISPEED, 38400);
+ SSH_ENCODE_OPT(TTY_OP_OSPEED, 38400);
+
+#undef SSH_ENCODE_OPT
+
+ /* end of options */
+ if (buflen > offset) {
+ buf[offset++] = TTY_OP_END;
+ } else {
+ return -1;
+ }
+
+ return (int)offset;
+}
+
+/**
+ * @ingroup libssh_misc
+ *
+ * @brief Encode the current TTY options as SSH modes.
+ *
+ * Call this function to determine the settings of the process' TTY and
+ * encode them as SSH Terminal Modes according to RFC 4254 section 8.
+ *
+ * If STDIN isn't connected to a TTY, this function fills the buffer with
+ * "sane" default modes.
+ *
+ * The encoded modes can be passed to \c ssh_channel_request_pty_size_modes .
+ *
+ * @code
+ * unsigned char modes_buf[SSH_TTY_MODES_MAX_BUFSIZE];
+ * encode_current_tty_opts(modes_buf, sizeof(modes_buf));
+ * @endcode
+ *
+ *
+ * @param[out] buf Modes will be encoded into this buffer.
+ *
+ * @param[in] buflen The length of the buffer.
+ *
+ * @return number of bytes in the buffer on success, -1 on error.
+ */
+int
+encode_current_tty_opts(unsigned char *buf, size_t buflen)
+{
+#ifdef HAVE_TERMIOS_H
+ struct termios attr;
+ ZERO_STRUCT(attr);
+
+ if (isatty(STDIN_FILENO)) {
+ /* get local terminal attributes */
+ if (tcgetattr(STDIN_FILENO, &attr) < 0) {
+ perror("tcgetattr");
+ return -1;
+ }
+ return encode_termios_opts(&attr, buf, buflen);
+ }
+#endif
+
+ /* use "sane" default attributes */
+ return encode_default_opts(buf, buflen);
+}
diff --git a/src/wrapper.c b/src/wrapper.c
index d53a61a3..bf949ea9 100644
--- a/src/wrapper.c
+++ b/src/wrapper.c
@@ -1,5 +1,5 @@
/*
- * wrapper.c - wrapper for crytpo functions
+ * wrapper.c - wrapper for crypto functions
*
* This file is part of the SSH Library
*
@@ -25,7 +25,7 @@
* Why a wrapper?
*
* Let's say you want to port libssh from libcrypto of openssl to libfoo
- * you are going to spend hours to remove every references to SHA1_Update()
+ * you are going to spend hours removing every reference to SHA1_Update()
* to libfoo_sha1_update after the work is finished, you're going to have
* only this file to modify it's not needed to say that your modifications
* are welcome.
@@ -108,7 +108,7 @@ const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type, bool etm)
}
/* it allocates a new cipher structure based on its offset into the global table */
-static struct ssh_cipher_struct *cipher_new(int offset) {
+static struct ssh_cipher_struct *cipher_new(uint8_t offset) {
struct ssh_cipher_struct *cipher = NULL;
cipher = malloc(sizeof(struct ssh_cipher_struct));
@@ -150,15 +150,16 @@ static void cipher_free(struct ssh_cipher_struct *cipher) {
SAFE_FREE(cipher);
}
-struct ssh_crypto_struct *crypto_new(void) {
- struct ssh_crypto_struct *crypto;
+struct ssh_crypto_struct *crypto_new(void)
+{
+ struct ssh_crypto_struct *crypto;
- crypto = malloc(sizeof(struct ssh_crypto_struct));
- if (crypto == NULL) {
- return NULL;
- }
- ZERO_STRUCTP(crypto);
- return crypto;
+ crypto = malloc(sizeof(struct ssh_crypto_struct));
+ if (crypto == NULL) {
+ return NULL;
+ }
+ ZERO_STRUCTP(crypto);
+ return crypto;
}
void crypto_free(struct ssh_crypto_struct *crypto)
@@ -176,17 +177,22 @@ void crypto_free(struct ssh_crypto_struct *crypto)
#ifdef HAVE_ECDH
SAFE_FREE(crypto->ecdh_client_pubkey);
SAFE_FREE(crypto->ecdh_server_pubkey);
- if(crypto->ecdh_privkey != NULL){
+ if (crypto->ecdh_privkey != NULL) {
#ifdef HAVE_OPENSSL_ECC
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_KEY_free(crypto->ecdh_privkey);
+#else
+ EVP_PKEY_free(crypto->ecdh_privkey);
+#endif /* OPENSSL_VERSION_NUMBER */
#elif defined HAVE_GCRYPT_ECC
gcry_sexp_release(crypto->ecdh_privkey);
#endif
crypto->ecdh_privkey = NULL;
}
#endif
+ SAFE_FREE(crypto->dh_server_signature);
if (crypto->session_id != NULL) {
- explicit_bzero(crypto->session_id, crypto->digest_len);
+ explicit_bzero(crypto->session_id, crypto->session_id_len);
SAFE_FREE(crypto->session_id);
}
if (crypto->secret_hash != NULL) {
@@ -233,12 +239,35 @@ void crypto_free(struct ssh_crypto_struct *crypto)
SAFE_FREE(crypto);
}
+static void
+compression_enable(ssh_session session,
+ enum ssh_crypto_direction_e direction,
+ bool delayed)
+{
+ /* The delayed compression is turned on AFTER authentication. This means
+ * that we need to turn it on immediately in case of rekeying */
+ if (delayed && !(session->flags & SSH_SESSION_FLAG_AUTHENTICATED)) {
+ if (direction == SSH_DIRECTION_IN) {
+ session->next_crypto->delayed_compress_in = 1;
+ } else { /* SSH_DIRECTION_OUT */
+ session->next_crypto->delayed_compress_out = 1;
+ }
+ } else {
+ if (direction == SSH_DIRECTION_IN) {
+ session->next_crypto->do_compress_in = 1;
+ } else { /* SSH_DIRECTION_OUT */
+ session->next_crypto->do_compress_out = 1;
+ }
+ }
+}
+
static int crypt_set_algorithms2(ssh_session session)
{
const char *wanted = NULL;
+ const char *method = NULL;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
- size_t i = 0;
+ uint8_t i = 0;
int cmp;
/*
@@ -358,22 +387,29 @@ static int crypt_set_algorithms2(ssh_session session)
session->next_crypto->in_hmac = ssh_hmactab[i].hmac_type;
session->next_crypto->in_hmac_etm = ssh_hmactab[i].etm;
- /* compression */
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib");
+ /* compression: client */
+ method = session->next_crypto->kex_methods[SSH_COMP_C_S];
+ cmp = strcmp(method, "zlib");
if (cmp == 0) {
- session->next_crypto->do_compress_out = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S compression");
+ compression_enable(session, SSH_DIRECTION_OUT, false);
}
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib");
+ cmp = strcmp(method, "zlib@openssh.com");
if (cmp == 0) {
- session->next_crypto->do_compress_in = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S delayed compression");
+ compression_enable(session, SSH_DIRECTION_OUT, true);
}
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib@openssh.com");
+
+ method = session->next_crypto->kex_methods[SSH_COMP_S_C];
+ cmp = strcmp(method, "zlib");
if (cmp == 0) {
- session->next_crypto->delayed_compress_out = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling S->C compression");
+ compression_enable(session, SSH_DIRECTION_IN, false);
}
- cmp = strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib@openssh.com");
+ cmp = strcmp(method, "zlib@openssh.com");
if (cmp == 0) {
- session->next_crypto->delayed_compress_in = 1;
+ SSH_LOG(SSH_LOG_PACKET, "enabling S->C delayed compression");
+ compression_enable(session, SSH_DIRECTION_IN, true);
}
return SSH_OK;
@@ -387,12 +423,11 @@ int crypt_set_algorithms_client(ssh_session session)
#ifdef WITH_SERVER
int crypt_set_algorithms_server(ssh_session session){
const char *method = NULL;
- size_t i = 0;
+ uint8_t i = 0;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
int cmp;
-
if (session == NULL) {
return SSH_ERROR;
}
@@ -511,33 +546,27 @@ int crypt_set_algorithms_server(ssh_session session){
/* compression */
method = session->next_crypto->kex_methods[SSH_COMP_C_S];
- if(strcmp(method,"zlib") == 0){
- SSH_LOG(SSH_LOG_PACKET,"enabling C->S compression");
- session->next_crypto->do_compress_in=1;
+ cmp = strcmp(method, "zlib");
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S compression");
+ compression_enable(session, SSH_DIRECTION_IN, false);
}
- if(strcmp(method,"zlib@openssh.com") == 0){
- SSH_LOG(SSH_LOG_PACKET,"enabling C->S delayed compression");
-
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
- session->next_crypto->do_compress_in = 1;
- } else {
- session->next_crypto->delayed_compress_in = 1;
- }
+ cmp = strcmp(method, "zlib@openssh.com");
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_PACKET, "enabling C->S delayed compression");
+ compression_enable(session, SSH_DIRECTION_IN, true);
}
method = session->next_crypto->kex_methods[SSH_COMP_S_C];
- if(strcmp(method,"zlib") == 0){
+ cmp = strcmp(method, "zlib");
+ if (cmp == 0) {
SSH_LOG(SSH_LOG_PACKET, "enabling S->C compression");
- session->next_crypto->do_compress_out=1;
+ compression_enable(session, SSH_DIRECTION_OUT, false);
}
- if(strcmp(method,"zlib@openssh.com") == 0){
- SSH_LOG(SSH_LOG_PACKET,"enabling S->C delayed compression");
-
- if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
- session->next_crypto->do_compress_out = 1;
- } else {
- session->next_crypto->delayed_compress_out = 1;
- }
+ cmp = strcmp(method, "zlib@openssh.com");
+ if (cmp == 0) {
+ SSH_LOG(SSH_LOG_PACKET, "enabling S->C delayed compression");
+ compression_enable(session, SSH_DIRECTION_OUT, true);
}
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];