mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-03-24 20:40:09 +09:00
Compare commits
11 Commits
e927820082
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
729a44e121 | ||
|
|
051ac812db | ||
|
|
01772c4f79 | ||
|
|
9f7c596ca5 | ||
|
|
34bbb48561 | ||
|
|
f060583d6f | ||
|
|
a05b2b76be | ||
|
|
c9f34ac55f | ||
|
|
bc24bba176 | ||
|
|
3154a4ab8d | ||
|
|
9478de8082 |
@@ -784,8 +784,6 @@ coverity:
|
||||
- mkdir obj && cd obj
|
||||
only:
|
||||
- branches@libssh/libssh-mirror
|
||||
- branches@cryptomilk/libssh-mirror
|
||||
- branches@jjelen/libssh-mirror
|
||||
|
||||
# TODO add -DFUZZ_TESTING=ON clang cant find _LLVMFuzzerInitialize on arm64
|
||||
macos-m1:
|
||||
|
||||
@@ -49,4 +49,4 @@ ALL_FUNC=$(echo "$FUNC_LINES" | sed -e "s/$F_CUT_BEFORE//g" -e "s/$F_CUT_AFTER//
|
||||
ALL_FUNC=$(echo "$ALL_FUNC" | sort - | uniq | wc -l)
|
||||
|
||||
# percentage of the documented functions
|
||||
awk "BEGIN {printf \"Documentation coverage is %.2f%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"
|
||||
awk "BEGIN {printf \"Documentation coverage is %.2f%%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* This is a sample implementation of a libssh based SSH server */
|
||||
/* This is a sample implementation of a libssh based SFTP server */
|
||||
/*
|
||||
Copyright 2014 Audrius Butkevicius
|
||||
|
||||
@@ -9,6 +9,28 @@ domain. This does not apply to the rest of the library though, but it is
|
||||
allowed to cut-and-paste working code from this file to any license of
|
||||
program.
|
||||
The goal is to show the API in action.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
|
||||
This is not a production-ready SFTP server implementation. While it demonstrates
|
||||
how an SFTP server can be implemented on the SFTP layer and integrated into
|
||||
existing SSH server, it lacks many steps int the authentication and
|
||||
session establishment!
|
||||
|
||||
It allows to log in any user with hardcoded credentials below or with public
|
||||
key provided from authorized keys file.
|
||||
|
||||
The resulting SFTP session keeps running under original user who runs the
|
||||
example server and therefore the SFTP session has access to all files that are
|
||||
accessible to the user running the server.
|
||||
|
||||
Real-world servers should at very least switch the user to unprivileged one
|
||||
after authentication using setuid(). If some more restrictions are needed,
|
||||
generally limiting what files should and should not be accessible, it is
|
||||
recommended to use chroot() as handling symlinks can be tricky in the SFTP
|
||||
callbacks.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -10,6 +10,23 @@ allowed to cut-and-paste working code from this file to any license of
|
||||
program.
|
||||
The goal is to show the API in action. It's not a reference on how terminal
|
||||
clients must be made or how a client should react.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
|
||||
This is not a production-ready SSH server implementation. While it demonstrates
|
||||
how an SSH server can be implemented, it lacks many steps during
|
||||
the authentication and session establishment!
|
||||
|
||||
It allows to log in any user with hardcoded credentials below or with public
|
||||
key provided from authorized keys file.
|
||||
|
||||
The resulting session keeps running under original user who runs the example
|
||||
server and therefore it retains the same permissions.
|
||||
|
||||
Real-world servers should at very least switch the user to unprivileged one
|
||||
after authentication using setuid().
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -9,6 +9,23 @@ domain. This does not apply to the rest of the library though, but it is
|
||||
allowed to cut-and-paste working code from this file to any license of
|
||||
program.
|
||||
The goal is to show the API in action.
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
|
||||
This is not a production-ready SSH server implementation. While it demonstrates
|
||||
how an SSH server can be implemented, it lacks many steps during
|
||||
the authentication and session establishment!
|
||||
|
||||
It allows to log in any user with hardcoded credentials below or with public
|
||||
key provided from authorized keys file.
|
||||
|
||||
The resulting session keeps running under original user who runs the example
|
||||
server and therefore it retains the same permissions.
|
||||
|
||||
Real-world servers should at very least switch the user to unprivileged one
|
||||
after authentication using setuid().
|
||||
|
||||
!!! WARNING / ACHTUNG !!!
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -57,10 +57,11 @@ enum ssh_bind_config_opcode_e {
|
||||
BIND_CFG_MAX /* Keep this one last in the list */
|
||||
};
|
||||
|
||||
/* @brief Parse configuration file and set the options to the given ssh_bind
|
||||
/**
|
||||
* @brief Parse configuration file and set the options to the given ssh_bind
|
||||
*
|
||||
* @params[in] sshbind The ssh_bind context to be configured
|
||||
* @params[in] filename The path to the configuration file
|
||||
* @param[in] sshbind The ssh_bind context to be configured
|
||||
* @param[in] filename The path to the configuration file
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
|
||||
@@ -538,12 +538,9 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
|
||||
* automatically passed through.
|
||||
*
|
||||
* @param list list of callbacks
|
||||
*
|
||||
* @param cbtype type of the callback
|
||||
*
|
||||
* @param c callback name
|
||||
*
|
||||
* @param va_args parameters to be passed
|
||||
* @param ... Parameters to be passed to the callback.
|
||||
*/
|
||||
#define ssh_callbacks_execute_list(list, cbtype, c, ...) \
|
||||
do { \
|
||||
@@ -585,9 +582,18 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
|
||||
_cb = ssh_iterator_value(_cb_type, _cb_i); \
|
||||
if (ssh_callbacks_exists(_cb, _cb_name))
|
||||
|
||||
/** @internal
|
||||
* @brief Execute the current callback in an ssh_callbacks_iterate() loop.
|
||||
*
|
||||
* @param _cb_name The name of the callback field to invoke.
|
||||
* @param ... Parameters to be passed to the callback.
|
||||
*/
|
||||
#define ssh_callbacks_iterate_exec(_cb_name, ...) \
|
||||
_cb->_cb_name(__VA_ARGS__, _cb->userdata)
|
||||
|
||||
/** @internal
|
||||
* @brief End an ssh_callbacks_iterate() loop.
|
||||
*/
|
||||
#define ssh_callbacks_iterate_end() \
|
||||
} \
|
||||
} while(0)
|
||||
@@ -1060,8 +1066,18 @@ LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Callback for thread mutex operations (init, destroy, lock, unlock).
|
||||
*
|
||||
* @param lock Pointer to the mutex lock.
|
||||
*
|
||||
* @return 0 on success, non-zero on error.
|
||||
*/
|
||||
typedef int (*ssh_thread_callback) (void **lock);
|
||||
|
||||
/** @brief Callback to retrieve the current thread identifier.
|
||||
*
|
||||
* @return The unique identifier of the calling thread.
|
||||
*/
|
||||
typedef unsigned long (*ssh_thread_id_callback) (void);
|
||||
struct ssh_threads_callbacks_struct {
|
||||
const char *type;
|
||||
@@ -1172,10 +1188,20 @@ typedef int (*ssh_jump_verify_knownhost_callback)(ssh_session session,
|
||||
typedef int (*ssh_jump_authenticate_callback)(ssh_session session,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Callback collection for managing an SSH proxyjump connection.
|
||||
*
|
||||
* Set these callbacks to control knownhost verification and authentication
|
||||
* on the jump host before the final destination is reached.
|
||||
*/
|
||||
struct ssh_jump_callbacks_struct {
|
||||
/** Userdata passed to each callback. */
|
||||
void *userdata;
|
||||
/** Called before connecting to the jump host. */
|
||||
ssh_jump_before_connection_callback before_connection;
|
||||
/** Called to verify the jump host's identity. */
|
||||
ssh_jump_verify_knownhost_callback verify_knownhost;
|
||||
/** Called to authenticate on the jump host. */
|
||||
ssh_jump_authenticate_callback authenticate;
|
||||
};
|
||||
|
||||
|
||||
@@ -70,9 +70,15 @@ struct ssh_iterator {
|
||||
const void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Holds connection details for an SSH proxyjump host.
|
||||
*/
|
||||
struct ssh_jump_info_struct {
|
||||
/** Hostname or IP address of the jump host. */
|
||||
char *hostname;
|
||||
/** Username to authenticate with on the jump host. */
|
||||
char *username;
|
||||
/** Port number of the jump host. */
|
||||
int port;
|
||||
};
|
||||
|
||||
|
||||
@@ -130,14 +130,15 @@ extern "C" {
|
||||
/* SSH Key Functions */
|
||||
void ssh_key_clean (ssh_key key);
|
||||
|
||||
const char *
|
||||
ssh_key_get_signature_algorithm(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name);
|
||||
const char *ssh_key_get_signature_algorithm(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type);
|
||||
enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
|
||||
enum ssh_keytypes_e type);
|
||||
enum ssh_digest_e ssh_key_hash_from_name(const char *name);
|
||||
|
||||
int ssh_key_type_and_hash_from_signature_name(const char *name,
|
||||
enum ssh_keytypes_e *type,
|
||||
enum ssh_digest_e *hash_type);
|
||||
|
||||
#define is_ecdsa_key_type(t) \
|
||||
((t) >= SSH_KEYTYPE_ECDSA_P256 && (t) <= SSH_KEYTYPE_ECDSA_P521)
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Options for configuring an SSH server bind session.
|
||||
*
|
||||
* Used with ssh_bind_options_set() to configure server-side options.
|
||||
*/
|
||||
enum ssh_bind_options_e {
|
||||
SSH_BIND_OPTIONS_BINDADDR,
|
||||
SSH_BIND_OPTIONS_BINDPORT,
|
||||
|
||||
@@ -64,6 +64,7 @@ extern "C" {
|
||||
#endif /* _MSC_VER */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/** @brief The SFTP protocol version implemented by this library. */
|
||||
#define LIBSFTP_VERSION 3
|
||||
|
||||
typedef struct sftp_attributes_struct* sftp_attributes;
|
||||
@@ -90,10 +91,76 @@ typedef struct sftp_request_queue_struct* sftp_request_queue;
|
||||
* @see sftp_free
|
||||
*/
|
||||
typedef struct sftp_session_struct* sftp_session;
|
||||
|
||||
/**
|
||||
* @brief Handle for an SFTP status message received from the server.
|
||||
*
|
||||
* This type represents a status response returned by the SFTP server in
|
||||
* reply to a client request. It carries a numeric status code and an optional
|
||||
* human-readable error message. This type is used internally by libssh and
|
||||
* is not part of the public API.
|
||||
*
|
||||
* @see sftp_status_message_struct
|
||||
*/
|
||||
typedef struct sftp_status_message_struct* sftp_status_message;
|
||||
|
||||
/**
|
||||
* @brief Handle for SFTP file system statistics.
|
||||
*
|
||||
* This type represents file system statistics as reported by the SFTP server,
|
||||
* analogous to the POSIX @c statvfs structure. It is obtained via
|
||||
* sftp_statvfs() or sftp_fstatvfs() and must be freed with
|
||||
* sftp_statvfs_free().
|
||||
*
|
||||
* @see sftp_statvfs_struct
|
||||
* @see sftp_statvfs
|
||||
* @see sftp_fstatvfs
|
||||
* @see sftp_statvfs_free
|
||||
*/
|
||||
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
|
||||
|
||||
/**
|
||||
* @brief Handle for SFTP server limits information.
|
||||
*
|
||||
* This type represents the server-reported SFTP limits such as the maximum
|
||||
* packet, read, and write lengths and the maximum number of open handles.
|
||||
* It is obtained via sftp_limits() and must be freed with sftp_limits_free().
|
||||
*
|
||||
* @see sftp_limits_struct
|
||||
* @see sftp_limits
|
||||
* @see sftp_limits_free
|
||||
*/
|
||||
typedef struct sftp_limits_struct* sftp_limits_t;
|
||||
|
||||
/**
|
||||
* @brief Handle for an asynchronous SFTP I/O operation.
|
||||
*
|
||||
* This type represents an in-flight asynchronous SFTP read or write request.
|
||||
* It is allocated by sftp_aio_begin_read() or sftp_aio_begin_write() and
|
||||
* consumed by the corresponding sftp_aio_wait_read() or sftp_aio_wait_write()
|
||||
* call. If the wait call is not reached, the handle must be freed explicitly
|
||||
* with sftp_aio_free() to avoid memory leaks.
|
||||
*
|
||||
* @see sftp_aio_begin_read
|
||||
* @see sftp_aio_wait_read
|
||||
* @see sftp_aio_begin_write
|
||||
* @see sftp_aio_wait_write
|
||||
* @see sftp_aio_free
|
||||
*/
|
||||
typedef struct sftp_aio_struct* sftp_aio;
|
||||
|
||||
/**
|
||||
* @brief Handle for an SFTP name-to-id mapping.
|
||||
*
|
||||
* This type stores a mapping between numeric user or group IDs and their
|
||||
* corresponding names. It is allocated via sftp_name_id_map_new(), populated
|
||||
* by sftp_get_users_groups_by_id(), and freed with sftp_name_id_map_free().
|
||||
*
|
||||
* @see sftp_name_id_map_struct
|
||||
* @see sftp_name_id_map_new
|
||||
* @see sftp_get_users_groups_by_id
|
||||
* @see sftp_name_id_map_free
|
||||
*/
|
||||
typedef struct sftp_name_id_map_struct *sftp_name_id_map;
|
||||
|
||||
struct sftp_session_struct {
|
||||
@@ -392,7 +459,6 @@ LIBSSH_API sftp_session sftp_new(ssh_session session);
|
||||
*/
|
||||
LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Close and deallocate a sftp session.
|
||||
*
|
||||
@@ -1582,7 +1648,9 @@ LIBSSH_API void sftp_handle_remove(sftp_session sftp, void *handle);
|
||||
#define SFTP_EXTENDED SSH_FXP_EXTENDED
|
||||
|
||||
/* openssh flags */
|
||||
/** @brief statvfs flag: file system is mounted read-only. */
|
||||
#define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */
|
||||
/** @brief statvfs flag: file system does not support setuid/setgid. */
|
||||
#define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -43,17 +43,35 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Macro to declare an SFTP message callback function.
|
||||
*
|
||||
* @param name The name of the callback function to declare.
|
||||
*/
|
||||
#define SSH_SFTP_CALLBACK(name) \
|
||||
static int name(sftp_client_message message)
|
||||
|
||||
/**
|
||||
* @brief Callback for handling SFTP client messages.
|
||||
*
|
||||
* @param message The SFTP client message to handle.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
typedef int (*sftp_server_message_callback)(sftp_client_message message);
|
||||
|
||||
/**
|
||||
* @brief Maps an SFTP message type to its handler callback.
|
||||
*/
|
||||
struct sftp_message_handler
|
||||
{
|
||||
/** The name of the SFTP operation (e.g. "read", "write"). */
|
||||
const char *name;
|
||||
/** The extended operation name for SSH_FXP_EXTENDED requests, or NULL. */
|
||||
const char *extended_name;
|
||||
/** The SFTP message type code (e.g. SSH_FXP_READ). */
|
||||
uint8_t type;
|
||||
|
||||
/** The callback function to invoke for this message type. */
|
||||
sftp_server_message_callback cb;
|
||||
};
|
||||
|
||||
@@ -61,6 +79,7 @@ LIBSSH_API int sftp_channel_default_subsystem_request(ssh_session session,
|
||||
ssh_channel channel,
|
||||
const char *subsystem,
|
||||
void *userdata);
|
||||
|
||||
LIBSSH_API int sftp_channel_default_data_callback(ssh_session session,
|
||||
ssh_channel channel,
|
||||
void *data,
|
||||
|
||||
16
src/buffer.c
16
src/buffer.c
@@ -109,6 +109,11 @@ static void buffer_verify(ssh_buffer buf)
|
||||
}
|
||||
|
||||
#else
|
||||
/** @internal
|
||||
* @brief No-op stub for buffer_verify when debug checks are disabled.
|
||||
*
|
||||
* @param x The buffer to verify (ignored).
|
||||
*/
|
||||
#define buffer_verify(x)
|
||||
#endif
|
||||
|
||||
@@ -964,9 +969,12 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
|
||||
|
||||
/** @internal
|
||||
* @brief Add multiple values in a buffer on a single function call
|
||||
*
|
||||
* @param[in] buffer The buffer to add to
|
||||
* @param[in] format A format string of arguments.
|
||||
* @param[in] argc Number of arguments passed after format.
|
||||
* @param[in] ap A va_list of arguments.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @see ssh_buffer_add_format() for format list values.
|
||||
@@ -1112,6 +1120,9 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
|
||||
* 'P': size_t, void * (len of data, pointer to data)
|
||||
* only pushes data.
|
||||
* 'B': bignum (pushed as SSH string)
|
||||
* @param[in] argc Number of arguments passed after format.
|
||||
* @param[in] ... Arguments as described by the format string.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @warning when using 'P' with a constant size (e.g. 8), do not
|
||||
@@ -1148,7 +1159,9 @@ int _ssh_buffer_pack(struct ssh_buffer_struct *buffer,
|
||||
* @brief Get multiple values from a buffer on a single function call
|
||||
* @param[in] buffer The buffer to get from
|
||||
* @param[in] format A format string of arguments.
|
||||
* @param[in] argc Number of arguments passed in the va_list.
|
||||
* @param[in] ap A va_list of arguments.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @see ssh_buffer_get_format() for format list values.
|
||||
@@ -1411,6 +1424,9 @@ cleanup:
|
||||
* 'P': size_t, void ** (len of data, pointer to data)
|
||||
* only pulls data.
|
||||
* 'B': bignum * (pulled as SSH string)
|
||||
* @param[in] argc Number of arguments passed after format.
|
||||
* @param[in] ... Arguments as described by the format string.
|
||||
*
|
||||
* @returns SSH_OK on success
|
||||
* SSH_ERROR on error
|
||||
* @warning when using 'P' with a constant size (e.g. 8), do not
|
||||
|
||||
@@ -1738,7 +1738,7 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
|
||||
*/
|
||||
int ssh_channel_is_open(ssh_channel channel)
|
||||
{
|
||||
if (channel == NULL) {
|
||||
if (channel == NULL || channel->session == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0);
|
||||
|
||||
23
src/config.c
23
src/config.c
@@ -1684,11 +1684,12 @@ int ssh_config_parse_line_cli(ssh_session session, const char *line)
|
||||
true);
|
||||
}
|
||||
|
||||
/* @brief Parse configuration from a file pointer
|
||||
/**
|
||||
* @brief Parse configuration from a file pointer
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] fp A valid file pointer
|
||||
* @params[in] global Whether the config is global or not
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] fp A valid file pointer
|
||||
* @param[in] global Whether the config is global or not
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
@@ -1710,10 +1711,11 @@ int ssh_config_parse(ssh_session session, FILE *fp, bool global)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration file and set the options to the given session
|
||||
/**
|
||||
* @brief Parse configuration file and set the options to the given session
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] filename The path to the ssh configuration file
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] filename The path to the ssh configuration file
|
||||
*
|
||||
* @returns 0 on successful parsing the configuration file, -1 on error
|
||||
*/
|
||||
@@ -1748,10 +1750,11 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* @brief Parse configuration string and set the options to the given session
|
||||
/**
|
||||
* @brief Parse configuration string and set the options to the given session
|
||||
*
|
||||
* @params[in] session The ssh session
|
||||
* @params[in] input Null terminated string containing the configuration
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] input Null terminated string containing the configuration
|
||||
*
|
||||
* @returns SSH_OK on successful parsing the configuration string,
|
||||
* SSH_ERROR on error
|
||||
|
||||
@@ -43,11 +43,9 @@
|
||||
* @brief Registers an error with a description.
|
||||
*
|
||||
* @param error The place to store the error.
|
||||
*
|
||||
* @param code The class of error.
|
||||
*
|
||||
* @param function The name of the calling function.
|
||||
* @param descr The description, which can be a format string.
|
||||
*
|
||||
* @param ... The arguments for the format string.
|
||||
*/
|
||||
void _ssh_set_error(void *error,
|
||||
@@ -76,6 +74,7 @@ void _ssh_set_error(void *error,
|
||||
* @brief Registers an out of memory error
|
||||
*
|
||||
* @param error The place to store the error.
|
||||
* @param function The name of the calling function.
|
||||
*
|
||||
*/
|
||||
void _ssh_set_error_oom(void *error, const char *function)
|
||||
|
||||
10
src/legacy.c
10
src/legacy.c
@@ -770,6 +770,16 @@ ssh_string ssh_get_pubkey(ssh_session session)
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
/**
|
||||
* @brief Accept an incoming SSH connection on a bind socket.
|
||||
*
|
||||
* @deprecated Use ssh_bind_accept() instead.
|
||||
*
|
||||
* @param session The SSH session to accept the connection on.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*/
|
||||
int ssh_accept(ssh_session session) {
|
||||
return ssh_handle_key_exchange(session);
|
||||
}
|
||||
|
||||
51
src/log.c
51
src/log.c
@@ -109,6 +109,16 @@ static void ssh_log_custom(ssh_logging_callback log_fn,
|
||||
log_fn(verbosity, function, buf, ssh_get_log_userdata());
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Dispatch a pre-formatted log message to the active logging backend.
|
||||
*
|
||||
* If a custom logging callback is registered, the message is passed to it.
|
||||
* Otherwise, the message is written to stderr.
|
||||
*
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param function The name of the calling function.
|
||||
* @param buffer The already-formatted log message string.
|
||||
*/
|
||||
void ssh_log_function(int verbosity,
|
||||
const char *function,
|
||||
const char *buffer)
|
||||
@@ -123,6 +133,15 @@ void ssh_log_function(int verbosity,
|
||||
ssh_log_stderr(verbosity, function, buffer);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Format a log message from a va_list and dispatch it for logging.
|
||||
*
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param function The name of the calling function.
|
||||
* @param format A printf-style format string.
|
||||
* @param va Pointer to the variable argument list
|
||||
* for the format string.
|
||||
*/
|
||||
void ssh_vlog(int verbosity,
|
||||
const char *function,
|
||||
const char *format,
|
||||
@@ -134,6 +153,15 @@ void ssh_vlog(int verbosity,
|
||||
ssh_log_function(verbosity, function, buffer);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Log a message if the given verbosity does not
|
||||
* exceed the global log level.
|
||||
*
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param function The name of the calling function.
|
||||
* @param format A printf-style format string.
|
||||
* @param ... Additional arguments corresponding to the format string.
|
||||
*/
|
||||
void _ssh_log(int verbosity,
|
||||
const char *function,
|
||||
const char *format, ...)
|
||||
@@ -149,6 +177,15 @@ void _ssh_log(int verbosity,
|
||||
|
||||
/* LEGACY */
|
||||
|
||||
/** @brief Log a message using the verbosity level of the given session.
|
||||
*
|
||||
* @deprecated Use the SSH_LOG() macro instead.
|
||||
*
|
||||
* @param session The SSH session whose verbosity level is checked.
|
||||
* @param verbosity The verbosity level of the message.
|
||||
* @param format A printf-style format string.
|
||||
* @param ... Arguments as described by the format string.
|
||||
*/
|
||||
void ssh_log(ssh_session session,
|
||||
int verbosity,
|
||||
const char *format, ...)
|
||||
@@ -164,9 +201,14 @@ void ssh_log(ssh_session session,
|
||||
|
||||
/** @internal
|
||||
* @brief log a SSH event with a common pointer
|
||||
* @param common The SSH/bind session.
|
||||
* @param verbosity The verbosity of the event.
|
||||
* @param format The format string of the log entry.
|
||||
*
|
||||
* Works for both ssh_session and ssh_bind as both embed ssh_common_struct.
|
||||
*
|
||||
* @param common The SSH/bind session.
|
||||
* @param verbosity The verbosity of the event.
|
||||
* @param function The name of the calling function.
|
||||
* @param format The format string of the log entry.
|
||||
* @param ... Additional arguments corresponding to the format string.
|
||||
*/
|
||||
void ssh_log_common(struct ssh_common_struct *common,
|
||||
int verbosity,
|
||||
@@ -221,6 +263,9 @@ int ssh_set_log_callback(ssh_logging_callback cb) {
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Clear the thread-local logging callback, reverting to stderr logging.
|
||||
*/
|
||||
void
|
||||
_ssh_reset_log_cb(void)
|
||||
{
|
||||
|
||||
151
src/misc.c
151
src/misc.c
@@ -127,7 +127,13 @@ static char *ssh_get_user_home_dir_internal(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we have read access on file */
|
||||
/** @internal
|
||||
* @brief Check whether the current process has read access to a file.
|
||||
*
|
||||
* @param[in] file Path to the file to check.
|
||||
*
|
||||
* @return 1 if the file is readable, 0 otherwise.
|
||||
*/
|
||||
int ssh_file_readaccess_ok(const char *file)
|
||||
{
|
||||
if (_access(file, 4) < 0) {
|
||||
@@ -208,11 +214,11 @@ struct tm *ssh_localtime(const time_t *timer, struct tm *result)
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get username from the calling process.
|
||||
/** @internal
|
||||
* @brief Get the username of the currently running process.
|
||||
*
|
||||
* @return An allocated string with the user on success, NULL on failure. The
|
||||
* caller is responsible for freeing returned string.
|
||||
* @return A newly allocated string with the username, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_local_username(void)
|
||||
{
|
||||
@@ -240,6 +246,13 @@ char *ssh_get_local_username(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if the string is a valid IPv4 address, 0 otherwise.
|
||||
*/
|
||||
int ssh_is_ipaddr_v4(const char *str)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
@@ -263,6 +276,13 @@ int ssh_is_ipaddr_v4(const char *str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 or IPv6 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if valid IP address, 0 if not, -1 on memory error.
|
||||
*/
|
||||
int ssh_is_ipaddr(const char *str)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
@@ -328,7 +348,13 @@ static char *ssh_get_user_home_dir_internal(void)
|
||||
return szPath;
|
||||
}
|
||||
|
||||
/* we have read access on file */
|
||||
/** @internal
|
||||
* @brief Check whether the current process has read access to a file.
|
||||
*
|
||||
* @param[in] file Path to the file to check.
|
||||
*
|
||||
* @return 1 if the file is readable, 0 otherwise.
|
||||
*/
|
||||
int ssh_file_readaccess_ok(const char *file)
|
||||
{
|
||||
if (access(file, R_OK) < 0) {
|
||||
@@ -363,11 +389,11 @@ int ssh_dir_writeable(const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get username from the calling process.
|
||||
/** @internal
|
||||
* @brief Get the username of the currently running process.
|
||||
*
|
||||
* @return An allocated string with the name on success, NULL on failure. The
|
||||
* caller is responsible for freeing returned string.
|
||||
* @return A newly allocated string with the username, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_local_username(void)
|
||||
{
|
||||
@@ -393,6 +419,13 @@ char *ssh_get_local_username(void)
|
||||
return name;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if the string is a valid IPv4 address, 0 otherwise.
|
||||
*/
|
||||
int ssh_is_ipaddr_v4(const char *str)
|
||||
{
|
||||
int rc = -1;
|
||||
@@ -406,6 +439,13 @@ int ssh_is_ipaddr_v4(const char *str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Check whether a string is a valid IPv4 or IPv6 address.
|
||||
*
|
||||
* @param[in] str The string to check.
|
||||
*
|
||||
* @return 1 if valid IP address, 0 if not, -1 on memory error.
|
||||
*/
|
||||
int ssh_is_ipaddr(const char *str)
|
||||
{
|
||||
int rc = -1;
|
||||
@@ -440,6 +480,17 @@ int ssh_is_ipaddr(const char *str)
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/** @internal
|
||||
* @brief Get the home directory of the current user.
|
||||
*
|
||||
* If a session is provided and a cached value exists, it is returned directly.
|
||||
* Otherwise the home directory is looked up and cached in the session.
|
||||
*
|
||||
* @param[in] session The SSH session to cache the result in, or NULL.
|
||||
*
|
||||
* @return A newly allocated string with the home directory path, or NULL
|
||||
* on error. The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_user_home_dir(ssh_session session)
|
||||
{
|
||||
char *szPath = NULL;
|
||||
@@ -463,6 +514,14 @@ char *ssh_get_user_home_dir(ssh_session session)
|
||||
return szPath;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Convert a string to lowercase.
|
||||
*
|
||||
* @param[in] str The string to convert.
|
||||
*
|
||||
* @return A newly allocated lowercase copy of the string, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_lowercase(const char* str)
|
||||
{
|
||||
char *new = NULL, *p = NULL;
|
||||
@@ -483,6 +542,15 @@ char *ssh_lowercase(const char* str)
|
||||
return new;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Format a host and port into a "[host]:port" string.
|
||||
*
|
||||
* @param[in] host The hostname or IP address.
|
||||
* @param[in] port The port number.
|
||||
*
|
||||
* @return A newly allocated string of the form "[host]:port", or NULL
|
||||
* on error. The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_hostport(const char *host, int port)
|
||||
{
|
||||
char *dest = NULL;
|
||||
@@ -788,6 +856,11 @@ const char *ssh_version(int req_version)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Create a new empty linked list.
|
||||
*
|
||||
* @return A newly allocated ssh_list, or NULL on memory error.
|
||||
*/
|
||||
struct ssh_list *ssh_list_new(void)
|
||||
{
|
||||
struct ssh_list *ret = malloc(sizeof(struct ssh_list));
|
||||
@@ -798,6 +871,13 @@ struct ssh_list *ssh_list_new(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Free a linked list and all its iterator nodes.
|
||||
*
|
||||
* The data pointed to by each node is not freed.
|
||||
*
|
||||
* @param[in] list The list to free.
|
||||
*/
|
||||
void ssh_list_free(struct ssh_list *list)
|
||||
{
|
||||
struct ssh_iterator *ptr = NULL, *next = NULL;
|
||||
@@ -812,6 +892,13 @@ void ssh_list_free(struct ssh_list *list)
|
||||
SAFE_FREE(list);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the first iterator of a linked list.
|
||||
*
|
||||
* @param[in] list The list to iterate.
|
||||
*
|
||||
* @return Pointer to the first iterator, or NULL if the list is empty.
|
||||
*/
|
||||
struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
|
||||
{
|
||||
if (!list)
|
||||
@@ -819,6 +906,14 @@ struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
|
||||
return list->root;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Find the iterator pointing to a specific value in the list.
|
||||
*
|
||||
* @param[in] list The list to search.
|
||||
* @param[in] value The data pointer to find.
|
||||
*
|
||||
* @return The iterator pointing to the value, or NULL if not found.
|
||||
*/
|
||||
struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value)
|
||||
{
|
||||
struct ssh_iterator *it = NULL;
|
||||
@@ -894,6 +989,14 @@ int ssh_list_append(struct ssh_list *list, const void *data)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Prepend an element at the beginning of the list.
|
||||
*
|
||||
* @param[in] list The list to prepend to.
|
||||
* @param[in] data The element to prepend.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_list_prepend(struct ssh_list *list, const void *data)
|
||||
{
|
||||
struct ssh_iterator *it = NULL;
|
||||
@@ -919,6 +1022,12 @@ int ssh_list_prepend(struct ssh_list *list, const void *data)
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Remove an element from the list by its iterator.
|
||||
*
|
||||
* @param[in] list The list to remove from.
|
||||
* @param[in] iterator The iterator pointing to the element to remove.
|
||||
*/
|
||||
void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator)
|
||||
{
|
||||
struct ssh_iterator *ptr = NULL, *prev = NULL;
|
||||
@@ -1257,6 +1366,12 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
return r;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the hostname of the local machine.
|
||||
*
|
||||
* @return A newly allocated string with the hostname, or NULL on error.
|
||||
* The caller is responsible for freeing it.
|
||||
*/
|
||||
char *ssh_get_local_hostname(void)
|
||||
{
|
||||
char host[NI_MAXHOST] = {0};
|
||||
@@ -1359,6 +1474,7 @@ err:
|
||||
/** @internal
|
||||
* @brief expands a string in function of session options
|
||||
*
|
||||
* @param[in] session The SSH session providing option values for expansion.
|
||||
* @param[in] s Format string to expand. Known parameters:
|
||||
* - %d user home directory (~)
|
||||
* - %h target host name
|
||||
@@ -1791,6 +1907,15 @@ void burn_free(void *ptr, size_t len)
|
||||
}
|
||||
|
||||
#if !defined(HAVE_STRNDUP)
|
||||
|
||||
/** @internal
|
||||
* @brief Compatibility implementation of strndup for systems that lack it.
|
||||
*
|
||||
* @param[in] s The string to duplicate.
|
||||
* @param[in] n Maximum number of characters to copy.
|
||||
*
|
||||
* @return A newly allocated null-terminated string, or NULL on error.
|
||||
*/
|
||||
char *strndup(const char *s, size_t n)
|
||||
{
|
||||
char *x = NULL;
|
||||
@@ -1811,7 +1936,11 @@ char *strndup(const char *s, size_t n)
|
||||
}
|
||||
#endif /* ! HAVE_STRNDUP */
|
||||
|
||||
/* Increment 64b integer in network byte order */
|
||||
/** @internal
|
||||
* @brief Increment a 64-bit counter stored in network byte order.
|
||||
*
|
||||
* @param[in,out] counter Pointer to an 8-byte buffer holding the counter.
|
||||
*/
|
||||
void
|
||||
uint64_inc(unsigned char *counter)
|
||||
{
|
||||
|
||||
@@ -288,6 +288,20 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set a key exchange algorithm list option on the session.
|
||||
*
|
||||
* Supports prefix modifiers: '+' to append, '-' to remove, '^' to prepend
|
||||
* to the default algorithm list.
|
||||
*
|
||||
* @param[in] session The SSH session.
|
||||
* @param[in] algo The algorithm type to configure.
|
||||
* @param[in] list The algorithm list string.
|
||||
* @param[out] place Pointer to the string to store
|
||||
* the resulting algorithm list.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_options_set_algo(ssh_session session,
|
||||
enum ssh_kex_types_e algo,
|
||||
const char *list,
|
||||
@@ -2081,6 +2095,16 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Apply default values for unset session options.
|
||||
*
|
||||
* Sets default SSH directory and username if not already configured,
|
||||
* and resolves any remaining option expansions.
|
||||
*
|
||||
* @param[in] session The SSH session to apply defaults to.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_options_apply(ssh_session session)
|
||||
{
|
||||
char *tmp = NULL;
|
||||
|
||||
180
src/pki.c
180
src/pki.c
@@ -449,40 +449,137 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
|
||||
/* We should never reach this */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum ssh_digest_e ssh_key_hash_from_name(const char *name)
|
||||
/**
|
||||
* @brief Convert a signature algorithm name to a key type and hash type.
|
||||
*
|
||||
* Looks up the given signature algorithm name and returns both the
|
||||
* corresponding key type and digest algorithm in a single call,
|
||||
* avoiding double string comparisons on the same input.
|
||||
*
|
||||
* @param[in] name The signature algorithm name to convert (e.g.
|
||||
* "ssh-rsa", "rsa-sha2-256", "ecdsa-sha2-nistp256").
|
||||
*
|
||||
* @param[out] type A pointer to store the resulting key type.
|
||||
*
|
||||
* @param[out] hash_type A pointer to store the resulting hash/digest type.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if name is NULL or
|
||||
* unknown.
|
||||
*/
|
||||
int ssh_key_type_and_hash_from_signature_name(const char *name,
|
||||
enum ssh_keytypes_e *type,
|
||||
enum ssh_digest_e *hash_type)
|
||||
{
|
||||
if (name == NULL) {
|
||||
/* TODO we should rather fail */
|
||||
return SSH_DIGEST_AUTO;
|
||||
size_t len;
|
||||
|
||||
if (name == NULL || type == NULL || hash_type == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (strcmp(name, "ssh-rsa") == 0) {
|
||||
return SSH_DIGEST_SHA1;
|
||||
} else if (strcmp(name, "rsa-sha2-256") == 0) {
|
||||
return SSH_DIGEST_SHA256;
|
||||
} else if (strcmp(name, "rsa-sha2-512") == 0) {
|
||||
return SSH_DIGEST_SHA512;
|
||||
} else if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
|
||||
return SSH_DIGEST_SHA256;
|
||||
} else if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
|
||||
return SSH_DIGEST_SHA384;
|
||||
} else if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
|
||||
return SSH_DIGEST_SHA512;
|
||||
} else if (strcmp(name, "ssh-ed25519") == 0) {
|
||||
return SSH_DIGEST_AUTO;
|
||||
} else if (strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
|
||||
return SSH_DIGEST_SHA256;
|
||||
} else if (strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
|
||||
return SSH_DIGEST_AUTO;
|
||||
len = strlen(name);
|
||||
|
||||
if (len == 7 && strcmp(name, "ssh-rsa") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA;
|
||||
*hash_type = SSH_DIGEST_SHA1;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 11 && strcmp(name, "ssh-ed25519") == 0) {
|
||||
*type = SSH_KEYTYPE_ED25519;
|
||||
*hash_type = SSH_DIGEST_AUTO;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 12) {
|
||||
if (strcmp(name, "rsa-sha2-256") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "rsa-sha2-512") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 19) {
|
||||
if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P256;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P384;
|
||||
*hash_type = SSH_DIGEST_SHA384;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P521;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 26 && strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_SK_ED25519;
|
||||
*hash_type = SSH_DIGEST_AUTO;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 28 && strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA1;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 32 && strcmp(name, "ssh-ed25519-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ED25519_CERT01;
|
||||
*hash_type = SSH_DIGEST_AUTO;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 33) {
|
||||
if (strcmp(name, "rsa-sha2-256-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "rsa-sha2-512-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_RSA_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 34 && strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_SK_ECDSA;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
if (len == 40) {
|
||||
if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P256_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA256;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P384_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA384;
|
||||
return SSH_OK;
|
||||
}
|
||||
if (strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) {
|
||||
*type = SSH_KEYTYPE_ECDSA_P521_CERT01;
|
||||
*hash_type = SSH_DIGEST_SHA512;
|
||||
return SSH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
SSH_LOG(SSH_LOG_TRACE, "Unknown signature name %s", name);
|
||||
|
||||
/* TODO we should rather fail */
|
||||
return SSH_DIGEST_AUTO;
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks the given key against the configured allowed
|
||||
* public key algorithm types
|
||||
@@ -700,27 +797,6 @@ ssh_key_get_signature_algorithm(ssh_session session,
|
||||
return ssh_key_signature_to_char(type, hash_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a ssh key algorithm name to a ssh key algorithm type.
|
||||
*
|
||||
* @param[in] name The name to convert.
|
||||
*
|
||||
* @return The enum ssh key algorithm type.
|
||||
*/
|
||||
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name) {
|
||||
if (name == NULL) {
|
||||
return SSH_KEYTYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
if ((strcmp(name, "rsa-sha2-256") == 0) ||
|
||||
(strcmp(name, "rsa-sha2-512") == 0)) {
|
||||
return SSH_KEYTYPE_RSA;
|
||||
}
|
||||
|
||||
/* Otherwise the key type matches the signature type */
|
||||
return ssh_key_type_from_name(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a ssh key name to a ssh key type.
|
||||
*
|
||||
@@ -2899,8 +2975,12 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
|
||||
}
|
||||
|
||||
alg = ssh_string_get_char(algorithm);
|
||||
type = ssh_key_type_from_signature_name(alg);
|
||||
hash_type = ssh_key_hash_from_name(alg);
|
||||
rc = ssh_key_type_and_hash_from_signature_name(alg, &type, &hash_type);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_BUFFER_FREE(buf);
|
||||
SSH_STRING_FREE(algorithm);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
SSH_STRING_FREE(algorithm);
|
||||
|
||||
blob = ssh_buffer_get_ssh_string(buf);
|
||||
|
||||
34
src/poll.c
34
src/poll.c
@@ -84,16 +84,33 @@ struct ssh_poll_ctx_struct {
|
||||
#ifdef HAVE_POLL
|
||||
#include <poll.h>
|
||||
|
||||
/** @internal
|
||||
* @brief Initialize the poll subsystem. No-op when native poll is available.
|
||||
*/
|
||||
void ssh_poll_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Clean up the poll subsystem. No-op when native poll is available.
|
||||
*/
|
||||
void ssh_poll_cleanup(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Wait for events on a set of file descriptors.
|
||||
*
|
||||
* @param fds Array of pollfd structures specifying the file descriptors.
|
||||
* @param nfds Number of file descriptors in the array.
|
||||
* @param timeout Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
|
||||
* to block indefinitely.
|
||||
*
|
||||
* @return Number of file descriptors with events, 0 on timeout,
|
||||
* -1 on error.
|
||||
*/
|
||||
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
|
||||
{
|
||||
return poll((struct pollfd *)fds, nfds, timeout);
|
||||
@@ -321,16 +338,33 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Initialize the poll subsystem using the BSD poll emulation.
|
||||
*/
|
||||
void ssh_poll_init(void)
|
||||
{
|
||||
ssh_poll_emu = bsd_poll;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Clean up the poll subsystem, resetting to the BSD poll emulation.
|
||||
*/
|
||||
void ssh_poll_cleanup(void)
|
||||
{
|
||||
ssh_poll_emu = bsd_poll;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Wait for events on a set of file descriptors.
|
||||
*
|
||||
* @param fds Array of pollfd structures specifying the file descriptors.
|
||||
* @param nfds Number of file descriptors in the array.
|
||||
* @param timeout Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
|
||||
* to block indefinitely.
|
||||
*
|
||||
* @return Number of file descriptors with events, 0 on timeout,
|
||||
* -1 on error.
|
||||
*/
|
||||
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
|
||||
{
|
||||
return (ssh_poll_emu)(fds, nfds, timeout);
|
||||
|
||||
@@ -1010,7 +1010,9 @@ int ssh_get_version(ssh_session session) {
|
||||
/**
|
||||
* @internal
|
||||
* @brief Callback to be called when the socket received an exception code.
|
||||
* @param user is a pointer to session
|
||||
* @param code The exception code from the socket layer.
|
||||
* @param errno_code The errno value associated with the exception.
|
||||
* @param user Pointer to the SSH session.
|
||||
*/
|
||||
void ssh_socket_exception_callback(int code, int errno_code, void *user){
|
||||
ssh_session session = (ssh_session)user;
|
||||
|
||||
431
src/sftpserver.c
431
src/sftpserver.c
@@ -24,11 +24,12 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <grp.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#endif
|
||||
|
||||
@@ -239,9 +240,7 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
||||
}
|
||||
break;
|
||||
case SSH_FXP_EXTENDED:
|
||||
rc = ssh_buffer_unpack(payload,
|
||||
"s",
|
||||
&msg->submessage);
|
||||
rc = ssh_buffer_unpack(payload, "s", &msg->submessage);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
@@ -256,9 +255,13 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
||||
goto error;
|
||||
}
|
||||
} else if (strcmp(msg->submessage, "statvfs@openssh.com") == 0 ){
|
||||
rc = ssh_buffer_unpack(payload,
|
||||
"s",
|
||||
&msg->filename);
|
||||
rc = ssh_buffer_unpack(payload, "s", &msg->filename);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
} else if (strcmp(msg->submessage,
|
||||
"users-groups-by-id@openssh.com") == 0) {
|
||||
rc = ssh_buffer_unpack(payload, "SS", &msg->data, &msg->handle);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
@@ -834,14 +837,17 @@ int sftp_reply_version(sftp_client_message client_msg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(reply, "dssssss",
|
||||
rc = ssh_buffer_pack(reply,
|
||||
"dssssssss",
|
||||
LIBSFTP_VERSION,
|
||||
"posix-rename@openssh.com",
|
||||
"1",
|
||||
"hardlink@openssh.com",
|
||||
"1",
|
||||
"statvfs@openssh.com",
|
||||
"2");
|
||||
"2",
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(session);
|
||||
SSH_BUFFER_FREE(reply);
|
||||
@@ -948,6 +954,14 @@ void *sftp_handle(sftp_session sftp, ssh_string handle)
|
||||
return sftp->handles[val];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove an SFTP file or directory handle from the session.
|
||||
*
|
||||
* @param sftp The SFTP session.
|
||||
* @param handle The handle to remove.
|
||||
*
|
||||
* @see sftp_handle_alloc()
|
||||
*/
|
||||
void sftp_handle_remove(sftp_session sftp, void *handle)
|
||||
{
|
||||
int i;
|
||||
@@ -1059,6 +1073,352 @@ struct sftp_handle
|
||||
char *name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Parse a blob of IDs into a sftp_name_id_map.
|
||||
*
|
||||
* This function extracts numeric IDs from the binary blob and populates
|
||||
* the 'ids' array of the map. Note that each element of the 'names'
|
||||
* array in the map is initialized to NULL by this function.
|
||||
*
|
||||
* @param[in] ids_blob The binary string blob containing uint32_t IDs.
|
||||
*
|
||||
* @return A newly allocated sftp_name_id_map on success, or NULL on error.
|
||||
*/
|
||||
static sftp_name_id_map sftp_name_id_map_from_ids_blob(ssh_string ids_blob)
|
||||
{
|
||||
sftp_name_id_map map = NULL;
|
||||
ssh_buffer buf = NULL;
|
||||
size_t len;
|
||||
uint32_t count, i;
|
||||
int rc;
|
||||
|
||||
if (ids_blob == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "IDs blob is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = ssh_string_len(ids_blob);
|
||||
|
||||
if (len % sizeof(uint32_t) != 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"IDs blob length is not a multiple of 4 bytes");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = len / sizeof(uint32_t);
|
||||
map = sftp_name_id_map_new(count);
|
||||
if (map == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate sftp_name_id_map");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = ssh_buffer_new();
|
||||
if (buf == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate ssh_buffer");
|
||||
sftp_name_id_map_free(map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_data(buf, ssh_string_data(ids_blob), len);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to copy blob data to buffer");
|
||||
SSH_BUFFER_FREE(buf);
|
||||
sftp_name_id_map_free(map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uint32_t val;
|
||||
rc = ssh_buffer_unpack(buf, "d", &val);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to unpack ID from buffer");
|
||||
SSH_BUFFER_FREE(buf);
|
||||
sftp_name_id_map_free(map);
|
||||
return NULL;
|
||||
}
|
||||
map->ids[i] = val;
|
||||
}
|
||||
|
||||
SSH_BUFFER_FREE(buf);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Fill the names array using UIDs in the map.
|
||||
*/
|
||||
static int sftp_fill_names_using_uids(sftp_name_id_map users_map)
|
||||
{
|
||||
struct passwd pwd_struct;
|
||||
struct passwd *pwd_res = NULL;
|
||||
long pwd_buf_size = -1;
|
||||
char *pwd_lookup_buf = NULL;
|
||||
uint32_t i;
|
||||
int rc = SSH_OK;
|
||||
|
||||
if (users_map == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#ifdef _SC_GETPW_R_SIZE_MAX
|
||||
pwd_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
#endif
|
||||
if (pwd_buf_size <= 0) {
|
||||
pwd_buf_size = 16384;
|
||||
}
|
||||
pwd_lookup_buf = calloc(1, pwd_buf_size);
|
||||
if (pwd_lookup_buf == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate pwd lookup buffer");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < users_map->count; i++) {
|
||||
int ret = getpwuid_r(users_map->ids[i],
|
||||
&pwd_struct,
|
||||
pwd_lookup_buf,
|
||||
pwd_buf_size,
|
||||
&pwd_res);
|
||||
|
||||
SAFE_FREE(users_map->names[i]);
|
||||
|
||||
if (ret == 0 && pwd_res != NULL) {
|
||||
users_map->names[i] = strdup(pwd_res->pw_name);
|
||||
} else {
|
||||
users_map->names[i] = strdup("");
|
||||
}
|
||||
|
||||
if (users_map->names[i] == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to allocate memory for username string");
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_FREE(pwd_lookup_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Fill the names array using GIDs in the map.
|
||||
*/
|
||||
static int sftp_fill_names_using_gids(sftp_name_id_map groups_map)
|
||||
{
|
||||
struct group grp_struct;
|
||||
struct group *grp_res = NULL;
|
||||
long grp_buf_size = -1;
|
||||
char *grp_lookup_buf = NULL;
|
||||
uint32_t i;
|
||||
int rc = SSH_OK;
|
||||
|
||||
if (groups_map == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
#ifdef _SC_GETGR_R_SIZE_MAX
|
||||
grp_buf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
#endif
|
||||
if (grp_buf_size <= 0) {
|
||||
grp_buf_size = 16384;
|
||||
}
|
||||
grp_lookup_buf = calloc(1, grp_buf_size);
|
||||
if (grp_lookup_buf == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate grp lookup buffer");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < groups_map->count; i++) {
|
||||
int ret = getgrgid_r(groups_map->ids[i],
|
||||
&grp_struct,
|
||||
grp_lookup_buf,
|
||||
grp_buf_size,
|
||||
&grp_res);
|
||||
|
||||
SAFE_FREE(groups_map->names[i]);
|
||||
|
||||
if (ret == 0 && grp_res != NULL) {
|
||||
groups_map->names[i] = strdup(grp_res->gr_name);
|
||||
} else {
|
||||
groups_map->names[i] = strdup("");
|
||||
}
|
||||
|
||||
if (groups_map->names[i] == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to allocate memory for group name string");
|
||||
rc = SSH_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_FREE(grp_lookup_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Pack the resolved names from a map into the output buffer.
|
||||
*
|
||||
* This function formats the multiple names according to the
|
||||
* users-groups-by-id@openssh.com extension specification. Each name in
|
||||
* the map is individually encoded as an SSH string. The entire concatenated
|
||||
* sequence of these encoded names is then wrapped and appended to the
|
||||
* output buffer as one single, large SSH string.
|
||||
*
|
||||
* @param[out] out_buffer The destination buffer for the final packed string.
|
||||
* @param[in] map The map containing the resolved names.
|
||||
*
|
||||
* @return SSH_OK on success, or SSH_ERROR on memory allocation failure.
|
||||
*/
|
||||
static int sftp_buffer_add_names(ssh_buffer out_buffer, sftp_name_id_map map)
|
||||
{
|
||||
ssh_buffer temp_buffer = NULL;
|
||||
uint32_t i;
|
||||
int rc;
|
||||
|
||||
if (out_buffer == NULL || map == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
temp_buffer = ssh_buffer_new();
|
||||
if (temp_buffer == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to allocate temporary buffer for names");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->count; i++) {
|
||||
const char *name = map->names[i] != NULL ? map->names[i] : "";
|
||||
rc = ssh_buffer_pack(temp_buffer, "s", name);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to pack name into buffer");
|
||||
SSH_BUFFER_FREE(temp_buffer);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(out_buffer,
|
||||
"dP",
|
||||
(uint32_t)ssh_buffer_get_len(temp_buffer),
|
||||
(size_t)ssh_buffer_get_len(temp_buffer),
|
||||
ssh_buffer_get(temp_buffer));
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING,
|
||||
"Failed to add names string blob to output buffer");
|
||||
}
|
||||
|
||||
SSH_BUFFER_FREE(temp_buffer);
|
||||
return (rc != SSH_OK) ? SSH_ERROR : SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Handle users-groups-by-id@openssh.com extension request.
|
||||
*
|
||||
* Resolves numeric user IDs (UIDs) and group IDs (GIDs) to their
|
||||
* corresponding username and group name strings. Returns empty strings
|
||||
* for IDs that cannot be resolved.
|
||||
*
|
||||
* @param[in] client_msg The SFTP client message containing the request.
|
||||
* client_msg->data contains the UIDs blob.
|
||||
* client_msg->handle contains the GIDs blob.
|
||||
*
|
||||
* @return SSH_OK on success (reply sent to client). On error, an error
|
||||
* status is sent to the client and SSH_ERROR is returned.
|
||||
*/
|
||||
static int process_users_groups_by_id(sftp_client_message client_msg)
|
||||
{
|
||||
ssh_buffer out = NULL;
|
||||
sftp_name_id_map users_map = NULL;
|
||||
sftp_name_id_map groups_map = NULL;
|
||||
int rc;
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Processing users-groups-by-id extension");
|
||||
|
||||
if (client_msg->data == NULL || client_msg->handle == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Missing UIDs or GIDs blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
users_map = sftp_name_id_map_from_ids_blob(client_msg->data);
|
||||
if (users_map == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to parse UIDs blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
groups_map = sftp_name_id_map_from_ids_blob(client_msg->handle);
|
||||
if (groups_map == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to parse GIDs blob");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_fill_names_using_uids(users_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to resolve UIDs");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_fill_names_using_gids(groups_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to resolve GIDs");
|
||||
goto error;
|
||||
}
|
||||
|
||||
out = ssh_buffer_new();
|
||||
if (out == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate output buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_add_u32(out, client_msg->id);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to add request ID to buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_buffer_add_names(out, users_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to add users to buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_buffer_add_names(out, groups_map);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to add groups to buffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = sftp_packet_write(client_msg->sftp, SSH_FXP_EXTENDED_REPLY, out);
|
||||
if (rc < 0) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "Failed to send extended reply");
|
||||
goto error;
|
||||
}
|
||||
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
SSH_BUFFER_FREE(out);
|
||||
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "Successfully processed request");
|
||||
return SSH_OK;
|
||||
|
||||
error:
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
SSH_BUFFER_FREE(out);
|
||||
SSH_LOG(SSH_LOG_WARNING, "Sending error response");
|
||||
|
||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Internal processing error");
|
||||
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
SSH_SFTP_CALLBACK(process_unsupported);
|
||||
SSH_SFTP_CALLBACK(process_open);
|
||||
SSH_SFTP_CALLBACK(process_read);
|
||||
@@ -1103,6 +1463,10 @@ const struct sftp_message_handler message_handlers[] = {
|
||||
const struct sftp_message_handler extended_handlers[] = {
|
||||
/* here are some extended type handlers */
|
||||
{"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
|
||||
{"users-groups-by-id",
|
||||
"users-groups-by-id@openssh.com",
|
||||
0,
|
||||
process_users_groups_by_id},
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
@@ -2071,16 +2435,19 @@ sftp_channel_default_subsystem_request(ssh_session session,
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default data callback for sftp server
|
||||
* @brief Default channel data callback for an SFTP server subsystem.
|
||||
*
|
||||
* @param[in] session The ssh session
|
||||
* @param[in] channel The ssh channel with SFTP session opened
|
||||
* @param[in] data The data to be processed.
|
||||
* @param[in] len The length of input data to be processed
|
||||
* @param[in] is_stderr Unused channel flag for stderr flagging
|
||||
* @param[in] userdata The pointer to sftp_session
|
||||
* Processes incoming data on the channel and dispatches it to the SFTP
|
||||
* server message handler.
|
||||
*
|
||||
* @return number of bytes processed, -1 when error occurs.
|
||||
* @param[in] session The SSH session.
|
||||
* @param[in] channel The SSH channel carrying the SFTP data.
|
||||
* @param[in] data The received data buffer.
|
||||
* @param[in] len The length of the data buffer.
|
||||
* @param[in] is_stderr Unused; SFTP does not use the stderr stream.
|
||||
* @param[in] userdata Pointer to the sftp_session handle.
|
||||
*
|
||||
* @return Number of bytes processed on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int
|
||||
sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||
@@ -2093,7 +2460,7 @@ sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||
sftp_session *sftpp = (sftp_session *)userdata;
|
||||
sftp_session sftp = NULL;
|
||||
sftp_client_message msg;
|
||||
int decode_len;
|
||||
uint32_t undecoded_len = len;
|
||||
int rc;
|
||||
|
||||
if (sftpp == NULL) {
|
||||
@@ -2102,17 +2469,25 @@ sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||
}
|
||||
sftp = *sftpp;
|
||||
|
||||
decode_len = sftp_decode_channel_data_to_packet(sftp, data, len);
|
||||
if (decode_len == SSH_ERROR)
|
||||
return SSH_ERROR;
|
||||
do {
|
||||
int decode_len =
|
||||
sftp_decode_channel_data_to_packet(sftp, data, undecoded_len);
|
||||
if (decode_len == SSH_ERROR) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
msg = sftp_get_client_message_from_packet(sftp);
|
||||
rc = process_client_message(msg);
|
||||
sftp_client_message_free(msg);
|
||||
if (rc != SSH_OK)
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
|
||||
msg = sftp_get_client_message_from_packet(sftp);
|
||||
rc = process_client_message(msg);
|
||||
sftp_client_message_free(msg);
|
||||
if (rc != SSH_OK) {
|
||||
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
|
||||
}
|
||||
|
||||
return decode_len;
|
||||
undecoded_len -= decode_len;
|
||||
data = (uint8_t *)data + decode_len;
|
||||
} while (undecoded_len > 0);
|
||||
|
||||
return len;
|
||||
}
|
||||
#else
|
||||
/* Not available on Windows for now */
|
||||
|
||||
31
src/socket.c
31
src/socket.c
@@ -65,6 +65,9 @@ struct sockaddr_un {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @internal
|
||||
* @brief Represents the possible states of an SSH socket connection.
|
||||
*/
|
||||
enum ssh_socket_states_e {
|
||||
SSH_SOCKET_NONE,
|
||||
SSH_SOCKET_CONNECTING,
|
||||
@@ -1063,12 +1066,26 @@ int ssh_socket_get_poll_flags(ssh_socket s)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to non-blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_socket_set_nonblocking(socket_t fd)
|
||||
{
|
||||
u_long nonblocking = 1;
|
||||
return ioctlsocket(fd, FIONBIO, &nonblocking);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_socket_set_blocking(socket_t fd)
|
||||
{
|
||||
u_long nonblocking = 0;
|
||||
@@ -1076,11 +1093,25 @@ int ssh_socket_set_blocking(socket_t fd)
|
||||
}
|
||||
|
||||
#else /* _WIN32 */
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to non-blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return `SSH_OK` on success, `SSH_ERROR` on error.
|
||||
*/
|
||||
int ssh_socket_set_nonblocking(socket_t fd)
|
||||
{
|
||||
return fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Set a socket file descriptor to blocking mode.
|
||||
*
|
||||
* @param fd The socket file descriptor to configure.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int ssh_socket_set_blocking(socket_t fd)
|
||||
{
|
||||
return fcntl(fd, F_SETFL, 0);
|
||||
|
||||
@@ -62,6 +62,9 @@ int ssh_threads_init(void)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Finalize and clean up the threading backend of the crypto libraries.
|
||||
*/
|
||||
void ssh_threads_finalize(void)
|
||||
{
|
||||
crypto_thread_finalize();
|
||||
@@ -83,6 +86,12 @@ int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief Get the type identifier of the currently active threading callbacks.
|
||||
*
|
||||
* @return A string identifying the threading backend, or NULL if no
|
||||
* callbacks are registered.
|
||||
*/
|
||||
const char *ssh_threads_get_type(void)
|
||||
{
|
||||
if (user_callbacks != NULL) {
|
||||
|
||||
@@ -437,6 +437,7 @@ int crypt_set_algorithms_server(ssh_session session){
|
||||
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
|
||||
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
|
||||
int cmp;
|
||||
int rc;
|
||||
|
||||
if (session == NULL) {
|
||||
return SSH_ERROR;
|
||||
@@ -580,8 +581,24 @@ int crypt_set_algorithms_server(ssh_session session){
|
||||
}
|
||||
|
||||
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];
|
||||
session->srv.hostkey = ssh_key_type_from_signature_name(method);
|
||||
session->srv.hostkey_digest = ssh_key_hash_from_name(method);
|
||||
|
||||
/* For GSSAPI key exchange, hostkey algorithm may be "null" */
|
||||
if (strcmp(method, "null") == 0) {
|
||||
session->srv.hostkey = SSH_KEYTYPE_UNKNOWN;
|
||||
session->srv.hostkey_digest = SSH_DIGEST_AUTO;
|
||||
} else {
|
||||
rc = ssh_key_type_and_hash_from_signature_name(
|
||||
method,
|
||||
&session->srv.hostkey,
|
||||
&session->srv.hostkey_digest);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(session,
|
||||
SSH_FATAL,
|
||||
"unknown hostkey algorithm %s",
|
||||
method);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* setup DH key exchange type */
|
||||
switch (session->next_crypto->kex_type) {
|
||||
|
||||
@@ -13,6 +13,9 @@ macro(fuzzer name)
|
||||
target_sources(${name} PRIVATE $<TARGET_OBJECTS:ssh_server_mock_obj>)
|
||||
target_link_libraries(${name} PRIVATE pthread)
|
||||
endif()
|
||||
if (WITH_COVERAGE)
|
||||
append_coverage_compiler_flags_to_target(${name})
|
||||
endif (WITH_COVERAGE)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set_target_properties(${name}
|
||||
|
||||
@@ -69,13 +69,53 @@ struct server_state_st {
|
||||
struct server_state_st *state);
|
||||
};
|
||||
|
||||
/*TODO: Add documentation */
|
||||
/**
|
||||
* @brief Free a server state struct.
|
||||
*
|
||||
* Frees all memory inside server_state_st using SAFE_FREE.
|
||||
*
|
||||
* @param[in] state The server_state_st struct to free.
|
||||
*/
|
||||
void free_server_state(struct server_state_st *state);
|
||||
|
||||
/*TODO: Add documentation */
|
||||
/**
|
||||
* @brief Run a SSH server based on a server state struct.
|
||||
*
|
||||
* Takes a server_state_st struct, validates required fields, and starts
|
||||
* listening for connections. For each client, it forks a child process
|
||||
* that calls handle_session. Blocks until SIGTERM is received.
|
||||
*
|
||||
* @param[in] state The server configuration struct.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR if an error occurred.
|
||||
*
|
||||
* @note This function blocks until SIGTERM is received.
|
||||
* @note The state is freed internally; do not use after calling.
|
||||
* @note If state->log_file is set, stdout/stderr are redirected to it.
|
||||
*
|
||||
* @see fork_run_server()
|
||||
* @see free_server_state()
|
||||
*/
|
||||
int run_server(struct server_state_st *state);
|
||||
|
||||
/*TODO: Add documentation */
|
||||
/**
|
||||
* @brief Fork and run an SSH server in non-blocking mode.
|
||||
*
|
||||
* Forks a child process that calls run_server(). The parent returns
|
||||
* immediately with the child's PID. Designed for tests that need
|
||||
* a server running in the background.
|
||||
*
|
||||
* @param[in] state The server_state_st struct passed to run_server().
|
||||
* @param[in] free_state Callback to free parent's test data in the child.
|
||||
* @param[in] userdata Pointer passed to free_state.
|
||||
*
|
||||
* @return Child PID on success, -1 on error.
|
||||
*
|
||||
* @note The parent should send SIGTERM to the child PID when done.
|
||||
* @note The state is freed by the child process via run_server().
|
||||
*
|
||||
* @see run_server()
|
||||
*/
|
||||
pid_t
|
||||
fork_run_server(struct server_state_st *state,
|
||||
void (*free_state) (void **userdata),
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
|
||||
#define LIBSSH_STATIC
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_VALGRIND_VALGRIND_H
|
||||
#include <valgrind/valgrind.h>
|
||||
@@ -48,6 +49,9 @@
|
||||
|
||||
#define TORTURE_KNOWN_HOSTS_FILE "libssh_torture_knownhosts"
|
||||
|
||||
#define TEST_INVALID_ID_1 99999
|
||||
#define TEST_INVALID_ID_2 88888
|
||||
|
||||
const char template[] = "temp_dir_XXXXXX";
|
||||
|
||||
struct test_server_st {
|
||||
@@ -71,9 +75,9 @@ static void free_test_server_state(void **state)
|
||||
|
||||
static int setup_default_server(void **state)
|
||||
{
|
||||
struct torture_state *s;
|
||||
struct server_state_st *ss;
|
||||
struct test_server_st *tss;
|
||||
struct torture_state *s = NULL;
|
||||
struct server_state_st *ss = NULL;
|
||||
struct test_server_st *tss = NULL;
|
||||
|
||||
char ed25519_hostkey[1024] = {0};
|
||||
char rsa_hostkey[1024];
|
||||
@@ -207,9 +211,9 @@ static int setup_default_server(void **state)
|
||||
|
||||
static int teardown_default_server(void **state)
|
||||
{
|
||||
struct torture_state *s;
|
||||
struct server_state_st *ss;
|
||||
struct test_server_st *tss;
|
||||
struct torture_state *s = NULL;
|
||||
struct server_state_st *ss = NULL;
|
||||
struct test_server_st *tss = NULL;
|
||||
|
||||
tss = *state;
|
||||
assert_non_null(tss);
|
||||
@@ -233,7 +237,7 @@ static int teardown_default_server(void **state)
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_state *s = NULL;
|
||||
int verbosity = torture_libssh_verbosity();
|
||||
char template2[] = "/tmp/ssh_torture_XXXXXX";
|
||||
char *cwd = NULL;
|
||||
@@ -335,7 +339,7 @@ static int session_setup_sftp(void **state)
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_state *s = NULL;
|
||||
int rc = 0;
|
||||
|
||||
assert_non_null(tss);
|
||||
@@ -365,10 +369,10 @@ static int session_teardown(void **state)
|
||||
static void torture_server_establish_sftp(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_sftp *tsftp;
|
||||
ssh_session session;
|
||||
sftp_session sftp;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
ssh_session session = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
@@ -418,17 +422,17 @@ static void torture_server_establish_sftp(void **state)
|
||||
static void torture_server_test_sftp_function(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_sftp *tsftp;
|
||||
ssh_session session;
|
||||
sftp_session sftp;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
ssh_session session = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
int rc;
|
||||
char *rv_str;
|
||||
sftp_dir dir;
|
||||
char *rv_str = NULL;
|
||||
sftp_dir dir = NULL;
|
||||
|
||||
char data[65535] = {0};
|
||||
sftp_file source;
|
||||
sftp_file to;
|
||||
sftp_file source = NULL;
|
||||
sftp_file to = NULL;
|
||||
int read_len;
|
||||
int write_len;
|
||||
|
||||
@@ -675,10 +679,10 @@ static void torture_server_sftp_open_read_write(void **state)
|
||||
static void torture_server_sftp_mkdir(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_sftp *tsftp;
|
||||
sftp_session sftp;
|
||||
ssh_session session;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
ssh_session session = NULL;
|
||||
sftp_file new_file = NULL;
|
||||
char tmp_dir[PATH_MAX] = {0};
|
||||
char tmp_file[PATH_MAX] = {0};
|
||||
@@ -750,10 +754,10 @@ static void torture_server_sftp_mkdir(void **state)
|
||||
static void torture_server_sftp_realpath(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_sftp *tsftp;
|
||||
sftp_session sftp;
|
||||
ssh_session session;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
ssh_session session = NULL;
|
||||
char path[PATH_MAX] = {0};
|
||||
char exp_path[PATH_MAX] = {0};
|
||||
char *new_path = NULL;
|
||||
@@ -799,10 +803,10 @@ static void torture_server_sftp_realpath(void **state)
|
||||
static void torture_server_sftp_symlink(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_sftp *tsftp;
|
||||
sftp_session sftp;
|
||||
ssh_session session;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
ssh_session session = NULL;
|
||||
sftp_file new_file = NULL;
|
||||
char tmp_dir[PATH_MAX] = {0};
|
||||
char tmp_file[PATH_MAX] = {0};
|
||||
@@ -811,7 +815,7 @@ static void torture_server_sftp_symlink(void **state)
|
||||
char data[42] = "012345678901234567890123456789012345678901";
|
||||
char *new_path = NULL;
|
||||
sftp_attributes a = NULL;
|
||||
sftp_dir dir;
|
||||
sftp_dir dir = NULL;
|
||||
int write_len, num_files = 0;
|
||||
int rc;
|
||||
|
||||
@@ -946,10 +950,10 @@ static void torture_server_sftp_symlink(void **state)
|
||||
static void torture_server_sftp_extended(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s;
|
||||
struct torture_sftp *tsftp;
|
||||
sftp_session sftp;
|
||||
ssh_session session;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_session sftp = NULL;
|
||||
ssh_session session = NULL;
|
||||
sftp_file new_file = NULL;
|
||||
char tmp_dir[PATH_MAX] = {0};
|
||||
char tmp_file[PATH_MAX] = {0};
|
||||
@@ -1095,7 +1099,7 @@ static void torture_server_sftp_handles_exhaustion(void **state)
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
char name[128] = {0};
|
||||
sftp_file handle, handles[SFTP_HANDLES] = {0};
|
||||
sftp_file handle = NULL, handles[SFTP_HANDLES] = {0};
|
||||
sftp_session sftp = NULL;
|
||||
int rc;
|
||||
|
||||
@@ -1300,6 +1304,274 @@ static void torture_server_sftp_payload_overrun(void **state)
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static void torture_sftp_users_groups_by_id_basic(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_name_id_map users_map = NULL;
|
||||
sftp_name_id_map groups_map = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
tsftp = s->ssh.tsftp;
|
||||
assert_non_null(tsftp);
|
||||
|
||||
rc = sftp_extension_supported(tsftp->sftp,
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
assert_int_equal(rc, 1);
|
||||
|
||||
users_map = sftp_name_id_map_new(2);
|
||||
assert_non_null(users_map);
|
||||
|
||||
groups_map = sftp_name_id_map_new(2);
|
||||
assert_non_null(groups_map);
|
||||
|
||||
users_map->ids[0] = TORTURE_SSH_USER_ID_BOB;
|
||||
users_map->ids[1] = TORTURE_SSH_USER_ID_ALICE;
|
||||
|
||||
groups_map->ids[0] = TORTURE_SSH_GROUP_ID_ROOT;
|
||||
groups_map->ids[1] = TORTURE_SSH_GROUP_ID_COMMON;
|
||||
|
||||
rc = sftp_get_users_groups_by_id(tsftp->sftp, users_map, groups_map);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_non_null(users_map->names[0]);
|
||||
assert_string_equal(users_map->names[0], TORTURE_SSH_USER_BOB);
|
||||
|
||||
assert_non_null(users_map->names[1]);
|
||||
assert_string_equal(users_map->names[1], TORTURE_SSH_USER_ALICE);
|
||||
|
||||
assert_non_null(groups_map->names[0]);
|
||||
assert_string_equal(groups_map->names[0], TORTURE_SSH_GROUP_ROOT);
|
||||
|
||||
assert_non_null(groups_map->names[1]);
|
||||
assert_string_equal(groups_map->names[1], TORTURE_SSH_GROUP_COMMON);
|
||||
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
}
|
||||
|
||||
static void torture_sftp_users_groups_by_id_unknown(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_name_id_map users_map = NULL;
|
||||
sftp_name_id_map groups_map = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
tsftp = s->ssh.tsftp;
|
||||
assert_non_null(tsftp);
|
||||
|
||||
rc = sftp_extension_supported(tsftp->sftp,
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
assert_int_equal(rc, 1);
|
||||
|
||||
users_map = sftp_name_id_map_new(1);
|
||||
assert_non_null(users_map);
|
||||
|
||||
groups_map = sftp_name_id_map_new(1);
|
||||
assert_non_null(groups_map);
|
||||
|
||||
/* Set User ID and Group ID that do not exist */
|
||||
users_map->ids[0] = TEST_INVALID_ID_1;
|
||||
groups_map->ids[0] = TEST_INVALID_ID_1;
|
||||
|
||||
rc = sftp_get_users_groups_by_id(tsftp->sftp, users_map, groups_map);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_non_null(users_map->names[0]);
|
||||
assert_string_equal(users_map->names[0], "");
|
||||
|
||||
assert_non_null(groups_map->names[0]);
|
||||
assert_string_equal(groups_map->names[0], "");
|
||||
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
}
|
||||
|
||||
static void torture_sftp_users_groups_by_id_users_only(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_name_id_map users_map = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
tsftp = s->ssh.tsftp;
|
||||
assert_non_null(tsftp);
|
||||
|
||||
rc = sftp_extension_supported(tsftp->sftp,
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
assert_int_equal(rc, 1);
|
||||
|
||||
users_map = sftp_name_id_map_new(1);
|
||||
assert_non_null(users_map);
|
||||
|
||||
users_map->ids[0] = TORTURE_SSH_USER_ID_ALICE;
|
||||
|
||||
rc = sftp_get_users_groups_by_id(tsftp->sftp, users_map, NULL);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_non_null(users_map->names[0]);
|
||||
assert_string_equal(users_map->names[0], TORTURE_SSH_USER_ALICE);
|
||||
|
||||
sftp_name_id_map_free(users_map);
|
||||
}
|
||||
|
||||
static void torture_sftp_users_groups_by_id_groups_only(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_name_id_map groups_map = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
tsftp = s->ssh.tsftp;
|
||||
assert_non_null(tsftp);
|
||||
|
||||
rc = sftp_extension_supported(tsftp->sftp,
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
assert_int_equal(rc, 1);
|
||||
|
||||
groups_map = sftp_name_id_map_new(1);
|
||||
assert_non_null(groups_map);
|
||||
|
||||
groups_map->ids[0] = TORTURE_SSH_GROUP_ID_ROOT;
|
||||
|
||||
rc = sftp_get_users_groups_by_id(tsftp->sftp, NULL, groups_map);
|
||||
assert_int_equal(rc, 0);
|
||||
assert_non_null(groups_map->names[0]);
|
||||
assert_string_equal(groups_map->names[0], TORTURE_SSH_GROUP_ROOT);
|
||||
|
||||
sftp_name_id_map_free(groups_map);
|
||||
}
|
||||
|
||||
static void torture_sftp_users_groups_by_id_multiple(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
sftp_name_id_map users_map = NULL;
|
||||
sftp_name_id_map groups_map = NULL;
|
||||
uint32_t i = 0;
|
||||
int rc;
|
||||
|
||||
struct {
|
||||
uid_t id;
|
||||
const char *name;
|
||||
} expected_users[] = {
|
||||
{TORTURE_SSH_USER_ID_BOB, TORTURE_SSH_USER_BOB},
|
||||
{TEST_INVALID_ID_1, ""},
|
||||
{TORTURE_SSH_USER_ID_ALICE, TORTURE_SSH_USER_ALICE},
|
||||
{TEST_INVALID_ID_2, ""},
|
||||
{TORTURE_SSH_USER_ID_CHARLIE, TORTURE_SSH_USER_CHARLIE}};
|
||||
size_t num_expected_users = ARRAY_SIZE(expected_users);
|
||||
|
||||
struct {
|
||||
gid_t id;
|
||||
const char *name;
|
||||
} expected_groups[] = {
|
||||
{TORTURE_SSH_GROUP_ID_ROOT, TORTURE_SSH_GROUP_ROOT},
|
||||
{TEST_INVALID_ID_1, ""},
|
||||
{TORTURE_SSH_GROUP_ID_COMMON, TORTURE_SSH_GROUP_COMMON},
|
||||
{TEST_INVALID_ID_2, ""},
|
||||
{TORTURE_SSH_GROUP_ID_SSHD, TORTURE_SSH_GROUP_SSHD}};
|
||||
size_t num_expected_groups = ARRAY_SIZE(expected_groups);
|
||||
|
||||
assert_non_null(tss);
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
tsftp = s->ssh.tsftp;
|
||||
assert_non_null(tsftp);
|
||||
|
||||
rc = sftp_extension_supported(tsftp->sftp,
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
assert_int_equal(rc, 1);
|
||||
|
||||
users_map = sftp_name_id_map_new(num_expected_users);
|
||||
assert_non_null(users_map);
|
||||
|
||||
groups_map = sftp_name_id_map_new(num_expected_groups);
|
||||
assert_non_null(groups_map);
|
||||
|
||||
for (i = 0; i < num_expected_users; i++) {
|
||||
users_map->ids[i] = expected_users[i].id;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_expected_groups; i++) {
|
||||
groups_map->ids[i] = expected_groups[i].id;
|
||||
}
|
||||
|
||||
rc = sftp_get_users_groups_by_id(tsftp->sftp, users_map, groups_map);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
for (i = 0; i < num_expected_users; i++) {
|
||||
assert_non_null(users_map->names[i]);
|
||||
assert_string_equal(users_map->names[i], expected_users[i].name);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_expected_groups; i++) {
|
||||
assert_non_null(groups_map->names[i]);
|
||||
assert_string_equal(groups_map->names[i], expected_groups[i].name);
|
||||
}
|
||||
|
||||
sftp_name_id_map_free(users_map);
|
||||
sftp_name_id_map_free(groups_map);
|
||||
}
|
||||
|
||||
static void torture_sftp_users_groups_by_id_negative(void **state)
|
||||
{
|
||||
struct test_server_st *tss = *state;
|
||||
struct torture_state *s = NULL;
|
||||
struct torture_sftp *tsftp = NULL;
|
||||
int rc;
|
||||
|
||||
assert_non_null(tss);
|
||||
|
||||
s = tss->state;
|
||||
assert_non_null(s);
|
||||
|
||||
tsftp = s->ssh.tsftp;
|
||||
assert_non_null(tsftp);
|
||||
|
||||
rc = sftp_extension_supported(tsftp->sftp,
|
||||
"users-groups-by-id@openssh.com",
|
||||
"1");
|
||||
assert_int_equal(rc, 1);
|
||||
|
||||
rc = sftp_get_users_groups_by_id(NULL, NULL, NULL);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
rc = sftp_get_users_groups_by_id(tsftp->sftp, NULL, NULL);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
}
|
||||
|
||||
int torture_run_tests(void) {
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
@@ -1330,15 +1602,38 @@ int torture_run_tests(void) {
|
||||
cmocka_unit_test_setup_teardown(torture_server_sftp_handles_exhaustion,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_server_sftp_opendir_handles_exhaustion,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_server_sftp_opendir_handles_exhaustion,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_server_sftp_handle_overrun,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_server_sftp_payload_overrun,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_users_groups_by_id_basic,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_users_groups_by_id_unknown,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_sftp_users_groups_by_id_users_only,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_sftp_users_groups_by_id_groups_only,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_sftp_users_groups_by_id_multiple,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
cmocka_unit_test_setup_teardown(
|
||||
torture_sftp_users_groups_by_id_negative,
|
||||
session_setup_sftp,
|
||||
session_teardown),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
@@ -53,6 +53,18 @@
|
||||
#define TORTURE_SSH_USER_CHARLIE "charlie"
|
||||
#define TORTURE_SSH_USER_NONEUSER "noneuser"
|
||||
|
||||
#define TORTURE_SSH_GROUP_ROOT "root"
|
||||
#define TORTURE_SSH_GROUP_COMMON "users"
|
||||
#define TORTURE_SSH_GROUP_SSHD "sshd"
|
||||
|
||||
#define TORTURE_SSH_USER_ID_BOB 5000
|
||||
#define TORTURE_SSH_USER_ID_ALICE 5001
|
||||
#define TORTURE_SSH_USER_ID_CHARLIE 5002
|
||||
|
||||
#define TORTURE_SSH_GROUP_ID_ROOT 0
|
||||
#define TORTURE_SSH_GROUP_ID_COMMON 9000
|
||||
#define TORTURE_SSH_GROUP_ID_SSHD 65531
|
||||
|
||||
/* Used by main to communicate with parse_opt. */
|
||||
struct argument_s {
|
||||
const char *pattern;
|
||||
|
||||
@@ -39,10 +39,29 @@ static void torture_channel_select(void **state)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void torture_channel_null_session(void **state)
|
||||
{
|
||||
ssh_channel channel = NULL;
|
||||
|
||||
(void)state;
|
||||
|
||||
channel = calloc(1, sizeof(struct ssh_channel_struct));
|
||||
|
||||
assert_non_null(channel);
|
||||
|
||||
channel->state = SSH_CHANNEL_STATE_OPEN;
|
||||
channel->session = NULL;
|
||||
|
||||
assert_int_equal(ssh_channel_is_open(channel), 0);
|
||||
|
||||
free(channel);
|
||||
}
|
||||
|
||||
int torture_run_tests(void) {
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(torture_channel_select),
|
||||
cmocka_unit_test(torture_channel_null_session),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
@@ -107,6 +107,76 @@ static void torture_pki_signature(void **state)
|
||||
ssh_signature_free(sig);
|
||||
}
|
||||
|
||||
static void torture_pki_type_and_hash_from_signature_name(void **state)
|
||||
{
|
||||
enum ssh_keytypes_e type;
|
||||
enum ssh_digest_e hash_type;
|
||||
int rc;
|
||||
|
||||
(void)state; /* unused */
|
||||
|
||||
/* Test NULL input */
|
||||
rc = ssh_key_type_and_hash_from_signature_name(NULL, &type, &hash_type);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
/* Test NULL output pointers */
|
||||
rc = ssh_key_type_and_hash_from_signature_name("ssh-rsa", NULL, &hash_type);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
rc = ssh_key_type_and_hash_from_signature_name("ssh-rsa", &type, NULL);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
/* Test unknown name */
|
||||
rc = ssh_key_type_and_hash_from_signature_name("unknown-algo",
|
||||
&type,
|
||||
&hash_type);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
/* Test valid RSA signatures */
|
||||
rc =
|
||||
ssh_key_type_and_hash_from_signature_name("ssh-rsa", &type, &hash_type);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_int_equal(type, SSH_KEYTYPE_RSA);
|
||||
assert_int_equal(hash_type, SSH_DIGEST_SHA1);
|
||||
|
||||
rc = ssh_key_type_and_hash_from_signature_name("rsa-sha2-256",
|
||||
&type,
|
||||
&hash_type);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_int_equal(type, SSH_KEYTYPE_RSA);
|
||||
assert_int_equal(hash_type, SSH_DIGEST_SHA256);
|
||||
|
||||
rc = ssh_key_type_and_hash_from_signature_name("rsa-sha2-512",
|
||||
&type,
|
||||
&hash_type);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_int_equal(type, SSH_KEYTYPE_RSA);
|
||||
assert_int_equal(hash_type, SSH_DIGEST_SHA512);
|
||||
|
||||
/* Test valid ECDSA signatures */
|
||||
rc = ssh_key_type_and_hash_from_signature_name("ecdsa-sha2-nistp256",
|
||||
&type,
|
||||
&hash_type);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_int_equal(type, SSH_KEYTYPE_ECDSA_P256);
|
||||
assert_int_equal(hash_type, SSH_DIGEST_SHA256);
|
||||
|
||||
/* Test valid ED25519 signature */
|
||||
rc = ssh_key_type_and_hash_from_signature_name("ssh-ed25519",
|
||||
&type,
|
||||
&hash_type);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_int_equal(type, SSH_KEYTYPE_ED25519);
|
||||
assert_int_equal(hash_type, SSH_DIGEST_AUTO);
|
||||
|
||||
/* Test that loose key names are rejected */
|
||||
rc = ssh_key_type_and_hash_from_signature_name("ecdsa", &type, &hash_type);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
|
||||
rc = ssh_key_type_and_hash_from_signature_name("rsa", &type, &hash_type);
|
||||
assert_int_equal(rc, SSH_ERROR);
|
||||
}
|
||||
|
||||
struct key_attrs {
|
||||
int sign;
|
||||
int verify;
|
||||
@@ -415,6 +485,7 @@ int torture_run_tests(void) {
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(torture_pki_keytype),
|
||||
cmocka_unit_test(torture_pki_signature),
|
||||
cmocka_unit_test(torture_pki_type_and_hash_from_signature_name),
|
||||
cmocka_unit_test_setup_teardown(torture_pki_verify_mismatch,
|
||||
setup_cert_dir,
|
||||
teardown_cert_dir),
|
||||
|
||||
Reference in New Issue
Block a user