mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-03-24 20:40:09 +09:00
OSS-Fuzz: Add fuzzer for scp functions
Signed-off-by: Arthur Chan <arthur.chan@adalogics.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
project(fuzzing CXX)
|
||||
|
||||
# Build SSH server mock helper as object library
|
||||
add_library(ssh_server_mock_obj OBJECT ssh_server_mock.c)
|
||||
target_link_libraries(ssh_server_mock_obj PRIVATE ${TORTURE_LINK_LIBRARIES})
|
||||
|
||||
macro(fuzzer name)
|
||||
add_executable(${name} ${name}.c)
|
||||
target_link_libraries(${name} PRIVATE ${TORTURE_LINK_LIBRARIES})
|
||||
|
||||
# Add ssh_server_mock dependency for scp and sftp fuzzers
|
||||
if (${name} STREQUAL "ssh_scp_fuzzer" OR ${name} STREQUAL "ssh_sftp_fuzzer")
|
||||
target_sources(${name} PRIVATE $<TARGET_OBJECTS:ssh_server_mock_obj>)
|
||||
target_link_libraries(${name} PRIVATE pthread)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set_target_properties(${name}
|
||||
PROPERTIES
|
||||
@@ -36,6 +47,7 @@ fuzzer(ssh_pubkey_fuzzer)
|
||||
fuzzer(ssh_sftp_attr_fuzzer)
|
||||
fuzzer(ssh_sshsig_fuzzer)
|
||||
if (WITH_SERVER)
|
||||
fuzzer(ssh_scp_fuzzer)
|
||||
fuzzer(ssh_server_fuzzer)
|
||||
fuzzer(ssh_bind_config_fuzzer)
|
||||
endif (WITH_SERVER)
|
||||
|
||||
206
tests/fuzz/ssh_scp_fuzzer.c
Normal file
206
tests/fuzz/ssh_scp_fuzzer.c
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2026 libssh authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LIBSSH_STATIC 1
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/scp.h>
|
||||
|
||||
#include "nallocinc.c"
|
||||
#include "ssh_server_mock.h"
|
||||
|
||||
static void _fuzz_finalize(void)
|
||||
{
|
||||
ssh_finalize();
|
||||
}
|
||||
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv)
|
||||
{
|
||||
(void)argc;
|
||||
nalloc_init(*argv[0]);
|
||||
ssh_init();
|
||||
atexit(_fuzz_finalize);
|
||||
ssh_mock_write_hostkey(SSH_MOCK_HOSTKEY_PATH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to test one cipher/HMAC combination */
|
||||
static int test_scp_with_cipher(const uint8_t *data,
|
||||
size_t size,
|
||||
const char *cipher,
|
||||
const char *hmac)
|
||||
{
|
||||
int socket_fds[2] = {-1, -1};
|
||||
ssh_session client_session = NULL;
|
||||
ssh_scp scp = NULL, scp_recursive = NULL;
|
||||
char buf[256] = {0};
|
||||
pthread_t srv_thread;
|
||||
|
||||
/* Configure mock SSH server with fuzzer data */
|
||||
struct ssh_mock_server_config server_config = {
|
||||
.protocol_data = data,
|
||||
.protocol_data_size = size,
|
||||
.exec_callback = ssh_mock_send_raw_data,
|
||||
.subsystem_callback = NULL,
|
||||
.callback_userdata = NULL,
|
||||
.cipher = cipher,
|
||||
.hmac = hmac,
|
||||
.server_socket = -1,
|
||||
.client_socket = -1,
|
||||
.server_ready = false,
|
||||
.server_error = false,
|
||||
};
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds) != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
server_config.server_socket = socket_fds[0];
|
||||
server_config.client_socket = socket_fds[1];
|
||||
|
||||
if (ssh_mock_server_start(&server_config, &srv_thread) != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
client_session = ssh_new();
|
||||
if (client_session == NULL) {
|
||||
goto cleanup_thread;
|
||||
}
|
||||
|
||||
/* Configure client with specified cipher/HMAC */
|
||||
ssh_options_set(client_session, SSH_OPTIONS_FD, &socket_fds[1]);
|
||||
ssh_options_set(client_session, SSH_OPTIONS_HOST, "localhost");
|
||||
ssh_options_set(client_session, SSH_OPTIONS_USER, "fuzz");
|
||||
ssh_options_set(client_session, SSH_OPTIONS_CIPHERS_C_S, cipher);
|
||||
ssh_options_set(client_session, SSH_OPTIONS_CIPHERS_S_C, cipher);
|
||||
ssh_options_set(client_session, SSH_OPTIONS_HMAC_C_S, hmac);
|
||||
ssh_options_set(client_session, SSH_OPTIONS_HMAC_S_C, hmac);
|
||||
|
||||
/* Set timeout for operations (1 second) */
|
||||
long timeout = 1;
|
||||
ssh_options_set(client_session, SSH_OPTIONS_TIMEOUT, &timeout);
|
||||
|
||||
if (ssh_connect(client_session) != SSH_OK) {
|
||||
goto cleanup_thread;
|
||||
}
|
||||
|
||||
if (ssh_userauth_none(client_session, NULL) != SSH_AUTH_SUCCESS) {
|
||||
goto cleanup_thread;
|
||||
}
|
||||
|
||||
scp = ssh_scp_new(client_session, SSH_SCP_READ, "/tmp/fuzz");
|
||||
if (scp == NULL) {
|
||||
goto cleanup_thread;
|
||||
}
|
||||
|
||||
if (ssh_scp_init(scp) != SSH_OK) {
|
||||
goto cleanup_thread;
|
||||
}
|
||||
|
||||
if (size > 0) {
|
||||
size_t copy_size = size < sizeof(buf) ? size : sizeof(buf);
|
||||
memcpy(buf, data, copy_size);
|
||||
}
|
||||
|
||||
/* Fuzz all SCP API functions in read mode */
|
||||
ssh_scp_pull_request(scp);
|
||||
ssh_scp_request_get_filename(scp);
|
||||
ssh_scp_request_get_permissions(scp);
|
||||
ssh_scp_request_get_size64(scp);
|
||||
ssh_scp_request_get_size(scp);
|
||||
ssh_scp_request_get_warning(scp);
|
||||
ssh_scp_accept_request(scp);
|
||||
ssh_scp_deny_request(scp, "Denied by fuzzer");
|
||||
ssh_scp_read(scp, buf, sizeof(buf));
|
||||
|
||||
/* Final fuzz of scp pull request after all the calls */
|
||||
ssh_scp_pull_request(scp);
|
||||
|
||||
/* Fuzz SCP in write/upload + recursive directory mode. */
|
||||
scp_recursive = ssh_scp_new(client_session,
|
||||
SSH_SCP_WRITE | SSH_SCP_RECURSIVE,
|
||||
"/tmp/fuzz-recursive");
|
||||
if (scp_recursive != NULL) {
|
||||
if (ssh_scp_init(scp_recursive) == SSH_OK) {
|
||||
ssh_scp_push_directory(scp_recursive, "fuzz-dir", 0755);
|
||||
ssh_scp_push_file(scp_recursive, "fuzz-file", sizeof(buf), 0644);
|
||||
ssh_scp_write(scp_recursive, buf, sizeof(buf));
|
||||
ssh_scp_leave_directory(scp_recursive);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup_thread:
|
||||
pthread_join(srv_thread, NULL);
|
||||
|
||||
cleanup:
|
||||
if (scp_recursive != NULL) {
|
||||
ssh_scp_close(scp_recursive);
|
||||
ssh_scp_free(scp_recursive);
|
||||
}
|
||||
if (scp) {
|
||||
ssh_scp_close(scp);
|
||||
ssh_scp_free(scp);
|
||||
}
|
||||
if (client_session) {
|
||||
ssh_disconnect(client_session);
|
||||
ssh_free(client_session);
|
||||
}
|
||||
if (socket_fds[0] >= 0)
|
||||
close(socket_fds[0]);
|
||||
if (socket_fds[1] >= 0)
|
||||
close(socket_fds[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
assert(nalloc_start(data, size) > 0);
|
||||
|
||||
/* Test all cipher/HMAC combinations exhaustively */
|
||||
const char *ciphers[] = {
|
||||
"none",
|
||||
"aes128-ctr",
|
||||
"aes256-ctr",
|
||||
"aes128-cbc",
|
||||
};
|
||||
|
||||
const char *hmacs[] = {
|
||||
"none",
|
||||
"hmac-sha1",
|
||||
"hmac-sha2-256",
|
||||
};
|
||||
|
||||
int num_ciphers = sizeof(ciphers) / sizeof(ciphers[0]);
|
||||
int num_hmacs = sizeof(hmacs) / sizeof(hmacs[0]);
|
||||
|
||||
for (int i = 0; i < num_ciphers; i++) {
|
||||
for (int j = 0; j < num_hmacs; j++) {
|
||||
test_scp_with_cipher(data, size, ciphers[i], hmacs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
nalloc_end();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
C0644 50 ../../../etc/passwd
|
||||
@@ -0,0 +1 @@
|
||||
C0644 10 dir/file.txt
|
||||
@@ -0,0 +1 @@
|
||||
C 100 test
|
||||
@@ -0,0 +1 @@
|
||||
C0644 10 ..
|
||||
@@ -0,0 +1 @@
|
||||
C0755 1024 executable.sh
|
||||
@@ -0,0 +1 @@
|
||||
C0644 999999999999 huge.dat
|
||||
@@ -0,0 +1 @@
|
||||
T1234567890 0 1234567890 0
|
||||
@@ -0,0 +1 @@
|
||||
C0644 100 test.txt
|
||||
@@ -0,0 +1 @@
|
||||
Warning: Test warning
|
||||
@@ -0,0 +1 @@
|
||||
C0644 10 .
|
||||
@@ -0,0 +1 @@
|
||||
E
|
||||
@@ -0,0 +1 @@
|
||||
Error: Test error
|
||||
@@ -0,0 +1 @@
|
||||
Xunknown command
|
||||
@@ -0,0 +1 @@
|
||||
C0644 test
|
||||
@@ -0,0 +1 @@
|
||||
D0755 0 mydir
|
||||
@@ -0,0 +1 @@
|
||||
C0644 abc test
|
||||
262
tests/fuzz/ssh_server_mock.c
Normal file
262
tests/fuzz/ssh_server_mock.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2026 libssh authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ssh_server_mock.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define LIBSSH_STATIC 1
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/server.h>
|
||||
|
||||
/* Fixed ed25519 key for all mock servers */
|
||||
const char *ssh_mock_ed25519_key_pem =
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n"
|
||||
"QyNTUxOQAAACBpFO8/JfYlIqg6+vqx1vDKWDqxJHxw4tBqnQfiOjf2zAAAAJgbsYq1G7GK\n"
|
||||
"tQAAAAtzc2gtZWQyNTUxOQAAACBpFO8/JfYlIqg6+vqx1vDKWDqxJHxw4tBqnQfiOjf2zA\n"
|
||||
"AAAEAkGaLvQwKMbGVRk2M8cz7gqWvpBKuHkuekJxIBQrUJl2kU7z8l9iUiqDr6+rHW8MpY\n"
|
||||
"OrEkfHDi0GqdB+I6N/bMAAAAEGZ1enotZWQyNTUxOS1rZXkBAgMEBQ==\n"
|
||||
"-----END OPENSSH PRIVATE KEY-----\n";
|
||||
|
||||
/* Internal server session data */
|
||||
struct mock_session_data {
|
||||
ssh_channel channel;
|
||||
struct ssh_mock_server_config *config;
|
||||
};
|
||||
|
||||
/* Auth callback - always accepts "none" auth */
|
||||
static int mock_auth_none(ssh_session session, const char *user, void *userdata)
|
||||
{
|
||||
(void)session;
|
||||
(void)user;
|
||||
(void)userdata;
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
|
||||
/* Channel open callback */
|
||||
static ssh_channel mock_channel_open(ssh_session session, void *userdata)
|
||||
{
|
||||
struct mock_session_data *sdata = (struct mock_session_data *)userdata;
|
||||
sdata->channel = ssh_channel_new(session);
|
||||
return sdata->channel;
|
||||
}
|
||||
|
||||
/* Exec request callback - for SCP */
|
||||
static int mock_channel_exec(ssh_session session,
|
||||
ssh_channel channel,
|
||||
const char *command,
|
||||
void *userdata)
|
||||
{
|
||||
struct mock_session_data *sdata = (struct mock_session_data *)userdata;
|
||||
(void)session;
|
||||
(void)command;
|
||||
|
||||
if (sdata->config->exec_callback) {
|
||||
return sdata->config->exec_callback(channel,
|
||||
sdata->config->protocol_data,
|
||||
sdata->config->protocol_data_size,
|
||||
sdata->config->callback_userdata);
|
||||
}
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/* Subsystem request callback - for SFTP */
|
||||
static int mock_channel_subsystem(ssh_session session,
|
||||
ssh_channel channel,
|
||||
const char *subsystem,
|
||||
void *userdata)
|
||||
{
|
||||
struct mock_session_data *sdata = (struct mock_session_data *)userdata;
|
||||
(void)session;
|
||||
(void)subsystem;
|
||||
|
||||
if (sdata->config->subsystem_callback) {
|
||||
return sdata->config->subsystem_callback(
|
||||
channel,
|
||||
sdata->config->protocol_data,
|
||||
sdata->config->protocol_data_size,
|
||||
sdata->config->callback_userdata);
|
||||
}
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/* Server thread implementation */
|
||||
static void *server_thread_func(void *arg)
|
||||
{
|
||||
struct ssh_mock_server_config *config =
|
||||
(struct ssh_mock_server_config *)arg;
|
||||
ssh_bind sshbind = NULL;
|
||||
ssh_session session = NULL;
|
||||
ssh_event event = NULL;
|
||||
struct mock_session_data sdata = {0};
|
||||
sdata.config = config;
|
||||
|
||||
struct ssh_server_callbacks_struct server_cb = {
|
||||
.userdata = &sdata,
|
||||
.auth_none_function = mock_auth_none,
|
||||
.channel_open_request_session_function = mock_channel_open,
|
||||
};
|
||||
|
||||
struct ssh_channel_callbacks_struct channel_cb = {
|
||||
.userdata = &sdata,
|
||||
.channel_exec_request_function = mock_channel_exec,
|
||||
.channel_subsystem_request_function = mock_channel_subsystem,
|
||||
};
|
||||
|
||||
bool no = false;
|
||||
|
||||
sshbind = ssh_bind_new();
|
||||
if (sshbind == NULL) {
|
||||
config->server_error = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session = ssh_new();
|
||||
if (session == NULL) {
|
||||
ssh_bind_free(sshbind);
|
||||
config->server_error = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *cipher = config->cipher ? config->cipher : "aes128-ctr";
|
||||
const char *hmac = config->hmac ? config->hmac : "hmac-sha1";
|
||||
|
||||
ssh_bind_options_set(sshbind,
|
||||
SSH_BIND_OPTIONS_HOSTKEY,
|
||||
SSH_MOCK_HOSTKEY_PATH);
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_C_S, cipher);
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_CIPHERS_S_C, cipher);
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_C_S, hmac);
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HMAC_S_C, hmac);
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_PROCESS_CONFIG, &no);
|
||||
|
||||
ssh_set_auth_methods(session, SSH_AUTH_METHOD_NONE);
|
||||
ssh_callbacks_init(&server_cb);
|
||||
ssh_set_server_callbacks(session, &server_cb);
|
||||
|
||||
if (ssh_bind_accept_fd(sshbind, session, config->server_socket) != SSH_OK) {
|
||||
ssh_free(session);
|
||||
ssh_bind_free(sshbind);
|
||||
config->server_error = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config->server_ready = true;
|
||||
|
||||
event = ssh_event_new();
|
||||
if (event == NULL) {
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
ssh_bind_free(sshbind);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ssh_handle_key_exchange(session) == SSH_OK) {
|
||||
ssh_event_add_session(event, session);
|
||||
|
||||
for (int i = 0; i < 50 && !sdata.channel; i++) {
|
||||
ssh_event_dopoll(event, 1);
|
||||
}
|
||||
|
||||
if (sdata.channel) {
|
||||
ssh_callbacks_init(&channel_cb);
|
||||
ssh_set_channel_callbacks(sdata.channel, &channel_cb);
|
||||
|
||||
int max_iterations = 30;
|
||||
for (int iter = 0; iter < max_iterations &&
|
||||
!ssh_channel_is_closed(sdata.channel) &&
|
||||
!ssh_channel_is_eof(sdata.channel);
|
||||
iter++) {
|
||||
if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event)
|
||||
ssh_event_free(event);
|
||||
if (session) {
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
}
|
||||
if (sshbind)
|
||||
ssh_bind_free(sshbind);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Public API - start mock SSH server */
|
||||
int ssh_mock_server_start(struct ssh_mock_server_config *config,
|
||||
pthread_t *thread)
|
||||
{
|
||||
if (!config || !thread)
|
||||
return -1;
|
||||
|
||||
config->server_ready = false;
|
||||
config->server_error = false;
|
||||
|
||||
if (pthread_create(thread, NULL, server_thread_func, config) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 50 && !config->server_ready && !config->server_error;
|
||||
i++) {
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
return config->server_error ? -1 : 0;
|
||||
}
|
||||
|
||||
/* Generic protocol callback - sends raw fuzzer data for any protocol */
|
||||
int ssh_mock_send_raw_data(void *channel,
|
||||
const void *data,
|
||||
size_t size,
|
||||
void *userdata)
|
||||
{
|
||||
(void)userdata;
|
||||
|
||||
ssh_channel target_channel = (ssh_channel)channel;
|
||||
|
||||
/* Send raw fuzzer data - let protocol parser interpret it */
|
||||
if (size > 0) {
|
||||
ssh_channel_write(target_channel, data, size);
|
||||
}
|
||||
|
||||
/* Close channel to signal completion */
|
||||
ssh_channel_send_eof(target_channel);
|
||||
ssh_channel_close(target_channel);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/* Write fixed ed25519 host key to file */
|
||||
int ssh_mock_write_hostkey(const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "wb");
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
size_t len = strlen(ssh_mock_ed25519_key_pem);
|
||||
size_t nwritten = fwrite(ssh_mock_ed25519_key_pem, 1, len, fp);
|
||||
fclose(fp);
|
||||
|
||||
return (nwritten == len) ? 0 : -1;
|
||||
}
|
||||
60
tests/fuzz/ssh_server_mock.h
Normal file
60
tests/fuzz/ssh_server_mock.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2026 libssh authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SSH_SERVER_MOCK_H
|
||||
#define SSH_SERVER_MOCK_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Server callback type */
|
||||
typedef int (*ssh_mock_callback_fn)(void *channel,
|
||||
const void *data,
|
||||
size_t size,
|
||||
void *userdata);
|
||||
|
||||
/* Mock server configuration */
|
||||
struct ssh_mock_server_config {
|
||||
const uint8_t *protocol_data;
|
||||
size_t protocol_data_size;
|
||||
ssh_mock_callback_fn exec_callback;
|
||||
ssh_mock_callback_fn subsystem_callback;
|
||||
void *callback_userdata;
|
||||
const char *cipher;
|
||||
const char *hmac;
|
||||
int server_socket;
|
||||
int client_socket;
|
||||
bool server_ready;
|
||||
bool server_error;
|
||||
};
|
||||
|
||||
/* Public API functions */
|
||||
int ssh_mock_server_start(struct ssh_mock_server_config *config,
|
||||
pthread_t *thread);
|
||||
int ssh_mock_send_raw_data(void *channel,
|
||||
const void *data,
|
||||
size_t size,
|
||||
void *userdata);
|
||||
int ssh_mock_write_hostkey(const char *path);
|
||||
|
||||
/* Fixed ed25519 key constant */
|
||||
extern const char *ssh_mock_ed25519_key_pem;
|
||||
|
||||
/* Centralized hostkey path used by all mock servers */
|
||||
#define SSH_MOCK_HOSTKEY_PATH "/tmp/libssh_mock_fuzz_key"
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user