mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-03-24 20:40:09 +09:00
Compare commits
3 Commits
3154a4ab8d
...
a05b2b76be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a05b2b76be | ||
|
|
c9f34ac55f | ||
|
|
bc24bba176 |
376
src/sftpserver.c
376
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);
|
||||
@@ -1067,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);
|
||||
@@ -1111,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},
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user