Adding support for limits@openssh.com on client side

Signed-off-by: anfanite396 <dipamt1729@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
anfanite396
2023-09-14 22:11:48 +05:30
committed by Jakub Jelen
parent 6cf5f0e340
commit 5d792a3b5a
4 changed files with 243 additions and 0 deletions

View File

@@ -77,6 +77,7 @@ typedef struct sftp_request_queue_struct* sftp_request_queue;
typedef struct sftp_session_struct* sftp_session; typedef struct sftp_session_struct* sftp_session;
typedef struct sftp_status_message_struct* sftp_status_message; typedef struct sftp_status_message_struct* sftp_status_message;
typedef struct sftp_statvfs_struct* sftp_statvfs_t; typedef struct sftp_statvfs_struct* sftp_statvfs_t;
typedef struct sftp_limits_struct* sftp_limits_t;
struct sftp_session_struct { struct sftp_session_struct {
ssh_session session; ssh_session session;
@@ -200,6 +201,16 @@ struct sftp_statvfs_struct {
uint64_t f_namemax; /** maximum filename length */ uint64_t f_namemax; /** maximum filename length */
}; };
/**
* @brief SFTP limits structure.
*/
struct sftp_limits_struct {
uint64_t max_packet_length; /** maximum number of bytes in a single sftp packet */
uint64_t max_read_length; /** maximum length in a SSH_FXP_READ packet */
uint64_t max_write_length; /** maximum length in a SSH_FXP_WRITE packet */
uint64_t max_open_handles; /** maximum number of active handles allowed by server */
};
/** /**
* @brief Creates a new sftp session. * @brief Creates a new sftp session.
* *
@@ -846,6 +857,24 @@ LIBSSH_API void sftp_statvfs_free(sftp_statvfs_t statvfs_o);
*/ */
LIBSSH_API int sftp_fsync(sftp_file file); LIBSSH_API int sftp_fsync(sftp_file file);
/**
* @brief Get information about the various limits the server might impose.
*
* @param sftp The sftp session handle.
*
* @return A limits structure or NULL on error.
*
* @see sftp_get_error()
*/
LIBSSH_API sftp_limits_t sftp_limits(sftp_session sftp);
/**
* @brief Free the memory of an allocated limits.
*
* @param limits The limits to free.
*/
LIBSSH_API void sftp_limits_free(sftp_limits_t limits);
/** /**
* @brief Canonicalize a sftp path. * @brief Canonicalize a sftp path.
* *

View File

@@ -3383,6 +3383,122 @@ void sftp_statvfs_free(sftp_statvfs_t statvfs) {
SAFE_FREE(statvfs); SAFE_FREE(statvfs);
} }
static sftp_limits_t
sftp_parse_limits(sftp_session sftp, ssh_buffer buf)
{
sftp_limits_t limits = NULL;
int rc;
limits = calloc(1, sizeof(struct sftp_limits_struct));
if (limits == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
rc = ssh_buffer_unpack(buf, "qqqq",
&limits->max_packet_length, /** maximum number of bytes in a single sftp packet */
&limits->max_read_length, /** maximum length in a SSH_FXP_READ packet */
&limits->max_write_length, /** maximum length in a SSH_FXP_WRITE packet */
&limits->max_open_handles /** maximum number of active handles allowed by server */
);
if (rc != SSH_OK) {
SAFE_FREE(limits);
ssh_set_error(sftp->session, SSH_FATAL, "Invalid limits structure");
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
return limits;
}
sftp_limits_t
sftp_limits(sftp_session sftp)
{
sftp_status_message status = NULL;
sftp_message msg = NULL;
ssh_buffer buffer;
uint32_t id;
int rc;
if (sftp == NULL)
return NULL;
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
id = sftp_get_new_id(sftp);
rc = ssh_buffer_pack(buffer,
"ds",
id,
"limits@openssh.com");
if (rc != SSH_OK) {
ssh_set_error_oom(sftp->session);
SSH_BUFFER_FREE(buffer);
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer);
SSH_BUFFER_FREE(buffer);
if (rc < 0) {
return NULL;
}
while (msg == NULL) {
if (sftp_read_and_dispatch(sftp) < 0) {
return NULL;
}
msg = sftp_dequeue(sftp, id);
}
if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) {
sftp_limits_t limits = sftp_parse_limits(sftp, msg->payload);
sftp_message_free(msg);
if (limits == NULL) {
return NULL;
}
return limits;
} else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
status = parse_status_msg(msg);
sftp_message_free(msg);
if (status == NULL) {
return NULL;
}
sftp_set_error(sftp, status->status);
ssh_set_error(sftp->session,
SSH_REQUEST_DENIED,
"SFTP server: %s",
status->errormsg);
status_msg_free(status);
} else { /* this shouldn't happen */
ssh_set_error(sftp->session,
SSH_FATAL,
"Received message %d when attempting to get limits",
msg->packet_type);
sftp_message_free(msg);
sftp_set_error(sftp, SSH_FX_BAD_MESSAGE);
}
return NULL;
}
void
sftp_limits_free(sftp_limits_t limits)
{
if (limits == NULL) {
return;
}
SAFE_FREE(limits);
}
/* another code written by Nick */ /* another code written by Nick */
char *sftp_canonicalize_path(sftp_session sftp, const char *path) char *sftp_canonicalize_path(sftp_session sftp, const char *path)
{ {

View File

@@ -51,6 +51,7 @@ if (WITH_SFTP)
torture_sftp_read torture_sftp_read
torture_sftp_fsync torture_sftp_fsync
torture_sftp_hardlink torture_sftp_hardlink
torture_sftp_limits
torture_sftp_rename torture_sftp_rename
${SFTP_BENCHMARK_TESTS}) ${SFTP_BENCHMARK_TESTS})
endif (WITH_SFTP) endif (WITH_SFTP)

View File

@@ -0,0 +1,97 @@
#define LIBSSH_STATIC
#include "config.h"
#include "torture.h"
#include "sftp.c"
#include <sys/types.h>
#include <pwd.h>
#include <errno.h>
static int sshd_setup(void **state)
{
torture_setup_sshd_server(state, false);
return 0;
}
static int sshd_teardown(void **state)
{
torture_teardown_sshd_server(state);
return 0;
}
static int session_setup(void **state)
{
struct torture_state *s = *state;
struct passwd *pwd = NULL;
int rc;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = torture_ssh_session(s,
TORTURE_SSH_SERVER,
NULL,
TORTURE_SSH_USER_ALICE,
NULL);
assert_non_null(s->ssh.session);
s->ssh.tsftp = torture_sftp_session(s->ssh.session);
assert_non_null(s->ssh.tsftp);
return 0;
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
torture_rmdirs(s->ssh.tsftp->testdir);
torture_sftp_close(s->ssh.tsftp);
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static void torture_sftp_limits(void **state)
{
struct torture_state *s = *state;
struct torture_sftp *t = s->ssh.tsftp;
sftp_limits_t li;
if (!sftp_extension_supported(t->sftp, "limits@openssh.com", "1"))
skip();
li = sftp_limits(t->sftp);
assert_non_null(li);
assert_int_not_equal(li->max_packet_length, 0);
assert_int_not_equal(li->max_read_length, 0);
assert_int_not_equal(li->max_write_length, 0);
assert_int_not_equal(li->max_open_handles, 0);
sftp_limits_free(li);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_sftp_limits,
session_setup,
session_teardown)
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
ssh_finalize();
return rc;
}