mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-04 12:20:42 +09:00
sftp: add users-groups-by-id@openssh.com extension for client
Signed-off-by: Praneeth Sarode <praneethsarode@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
This commit is contained in:
@@ -79,6 +79,7 @@ typedef struct sftp_status_message_struct* sftp_status_message;
|
||||
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
|
||||
typedef struct sftp_limits_struct* sftp_limits_t;
|
||||
typedef struct sftp_aio_struct* sftp_aio;
|
||||
typedef struct sftp_name_id_map_struct *sftp_name_id_map;
|
||||
|
||||
struct sftp_session_struct {
|
||||
ssh_session session;
|
||||
@@ -213,6 +214,22 @@ struct sftp_limits_struct {
|
||||
uint64_t max_open_handles; /** maximum number of active handles allowed by server */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SFTP names map structure to store the mapping between ids and names.
|
||||
*
|
||||
* This is mainly for the use of sftp_get_users_groups_by_id() function.
|
||||
*/
|
||||
struct sftp_name_id_map_struct {
|
||||
/** @brief Count of name-id pairs in the map */
|
||||
uint32_t count;
|
||||
|
||||
/** @brief Array of ids, ids[i] mapped to names[i] */
|
||||
uint32_t *ids;
|
||||
|
||||
/** @brief Array of names, names[i] mapped to ids[i] */
|
||||
char **names;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a new sftp session.
|
||||
*
|
||||
@@ -1194,6 +1211,58 @@ LIBSSH_API char *sftp_expand_path(sftp_session sftp, const char *path);
|
||||
*/
|
||||
LIBSSH_API char *sftp_home_directory(sftp_session sftp, const char *username);
|
||||
|
||||
/**
|
||||
* @brief Create a new sftp_name_id_map struct.
|
||||
*
|
||||
* @param count The number of ids/names to store in the map.
|
||||
*
|
||||
* @return A pointer to the newly allocated sftp_name_id_map
|
||||
* struct.
|
||||
*/
|
||||
LIBSSH_API sftp_name_id_map sftp_name_id_map_new(uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Free the memory of an allocated `sftp_name_id_map` struct.
|
||||
*
|
||||
* @param map A pointer to the `sftp_name_id_map` struct to free.
|
||||
*/
|
||||
LIBSSH_API void sftp_name_id_map_free(sftp_name_id_map map);
|
||||
|
||||
/**
|
||||
* @brief Retrieves usernames and group names based on provided user and group
|
||||
* IDs.
|
||||
*
|
||||
* The retrieved names are stored in the `names` field of the
|
||||
* `sftp_name_id_map` structure. In case a uid or gid is not found, an empty
|
||||
* string is stored.
|
||||
*
|
||||
* This calls the "users-groups-by-id@openssh.com" extension.
|
||||
* You should check if the extension is supported using:
|
||||
*
|
||||
* @code
|
||||
* int supported = sftp_extension_supported(sftp,
|
||||
* "users-groups-by-id@openssh.com", "1");
|
||||
* @endcode
|
||||
*
|
||||
* @param sftp The SFTP session handle.
|
||||
*
|
||||
* @param users_map A pointer to a `sftp_name_id_map` struct with the user
|
||||
* IDs. Can be NULL if only group names are needed.
|
||||
*
|
||||
* @param groups_map A pointer to a `sftp_name_id_map` struct with the group
|
||||
* IDs. Can be NULL if only user names are needed.
|
||||
*
|
||||
* @return 0 on success, < 0 on error with ssh and sftp error set.
|
||||
*
|
||||
* @note The caller needs to free the memory used for
|
||||
* the maps later using `sftp_name_id_map_free()`.
|
||||
*
|
||||
* @see sftp_get_error()
|
||||
*/
|
||||
LIBSSH_API int sftp_get_users_groups_by_id(sftp_session sftp,
|
||||
sftp_name_id_map users_map,
|
||||
sftp_name_id_map groups_map);
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
/**
|
||||
* @brief Create a new sftp server session.
|
||||
|
||||
@@ -481,3 +481,11 @@ LIBSSH_4_10_0 # Released
|
||||
ssh_pki_export_privkey_file_format;
|
||||
ssh_request_no_more_sessions;
|
||||
} LIBSSH_4_9_0;
|
||||
|
||||
LIBSSH_AFTER_4_10_0
|
||||
{
|
||||
global:
|
||||
sftp_get_users_groups_by_id;
|
||||
sftp_name_id_map_free;
|
||||
sftp_name_id_map_new;
|
||||
} LIBSSH_4_10_0;
|
||||
|
||||
212
src/sftp.c
212
src/sftp.c
@@ -3305,4 +3305,216 @@ sftp_home_directory(sftp_session sftp, const char *username)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sftp_name_id_map sftp_name_id_map_new(uint32_t count)
|
||||
{
|
||||
sftp_name_id_map map = NULL;
|
||||
|
||||
map = calloc(1, sizeof(struct sftp_name_id_map_struct));
|
||||
if (map == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
map->count = count;
|
||||
|
||||
map->ids = calloc(count, sizeof(uint32_t));
|
||||
if (map->ids == NULL) {
|
||||
SAFE_FREE(map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
map->names = calloc(count, sizeof(char *));
|
||||
if (map->names == NULL) {
|
||||
SAFE_FREE(map->ids);
|
||||
SAFE_FREE(map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void sftp_name_id_map_free(sftp_name_id_map map)
|
||||
{
|
||||
if (map == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SAFE_FREE(map->ids);
|
||||
|
||||
if (map->names != NULL) {
|
||||
for (uint32_t i = 0; i < map->count; i++) {
|
||||
SAFE_FREE(map->names[i]);
|
||||
}
|
||||
SAFE_FREE(map->names);
|
||||
}
|
||||
|
||||
SAFE_FREE(map);
|
||||
}
|
||||
|
||||
static int sftp_buffer_add_ids(ssh_buffer buffer, sftp_name_id_map map)
|
||||
{
|
||||
uint32_t id_count = map ? map->count : 0;
|
||||
int rc;
|
||||
|
||||
rc = ssh_buffer_pack(buffer, "d", sizeof(uint32_t) * id_count);
|
||||
if (rc != SSH_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < id_count; i++) {
|
||||
rc = ssh_buffer_pack(buffer, "d", map->ids[i]);
|
||||
if (rc != SSH_OK) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sftp_parse_names(ssh_buffer buffer, sftp_name_id_map map)
|
||||
{
|
||||
uint32_t name_buf_len = 0;
|
||||
char *name = NULL;
|
||||
uint32_t id_count = map ? map->count : 0;
|
||||
int rc;
|
||||
|
||||
rc = ssh_buffer_unpack(buffer, "d", &name_buf_len);
|
||||
if (rc != SSH_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < id_count; i++) {
|
||||
rc = ssh_buffer_unpack(buffer, "s", &name);
|
||||
if (rc != SSH_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
name_buf_len -= strlen(name) + sizeof(uint32_t);
|
||||
map->names[i] = name;
|
||||
}
|
||||
|
||||
if (name_buf_len != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sftp_get_users_groups_by_id(sftp_session sftp,
|
||||
sftp_name_id_map users_map,
|
||||
sftp_name_id_map groups_map)
|
||||
{
|
||||
sftp_status_message status = NULL;
|
||||
sftp_message msg = NULL;
|
||||
ssh_buffer buffer = NULL;
|
||||
uint32_t id;
|
||||
int rc;
|
||||
|
||||
if (sftp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check if the user has provided the correct arguments */
|
||||
if (users_map == NULL && groups_map == NULL) {
|
||||
ssh_set_error(sftp->session,
|
||||
SSH_FATAL,
|
||||
"Both users map and groups map cannot be NULL");
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer = ssh_buffer_new();
|
||||
if (buffer == NULL) {
|
||||
ssh_set_error_oom(sftp->session);
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
id = sftp_get_new_id(sftp);
|
||||
|
||||
rc = ssh_buffer_pack(buffer, "ds", id, "users-groups-by-id@openssh.com");
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(sftp->session);
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* pack all uids */
|
||||
rc = sftp_buffer_add_ids(buffer, users_map);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(sftp->session);
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* pack all gids */
|
||||
rc = sftp_buffer_add_ids(buffer, groups_map);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error_oom(sftp->session);
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
|
||||
SSH_BUFFER_FREE(buffer);
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = sftp_recv_response_msg(sftp, id, true, &msg);
|
||||
if (rc != SSH_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
|
||||
rc = sftp_parse_names(msg->payload, users_map);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(sftp->session,
|
||||
SSH_ERROR,
|
||||
"Failed to parse usernames");
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
sftp_message_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = sftp_parse_names(msg->payload, groups_map);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_set_error(sftp->session,
|
||||
SSH_ERROR,
|
||||
"Failed to parse groupnames");
|
||||
sftp_set_error(sftp, SSH_FX_FAILURE);
|
||||
sftp_message_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sftp_message_free(msg);
|
||||
return 0;
|
||||
} else if (msg->packet_type == SSH_FXP_STATUS) {
|
||||
status = parse_status_msg(msg);
|
||||
sftp_message_free(msg);
|
||||
if (status == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sftp_set_error(sftp, status->status);
|
||||
ssh_set_error(sftp->session,
|
||||
SSH_REQUEST_DENIED,
|
||||
"SFTP server: %s",
|
||||
status->errormsg);
|
||||
status_msg_free(status);
|
||||
} else {
|
||||
ssh_set_error(sftp->session,
|
||||
SSH_FATAL,
|
||||
"Received message %d when attempting to get user and "
|
||||
"group names by id",
|
||||
msg->packet_type);
|
||||
sftp_message_free(msg);
|
||||
sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* WITH_SFTP */
|
||||
|
||||
Reference in New Issue
Block a user