Skip to content
Snippets Groups Projects
Commit b9a65f47 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

The start of the transport layer and the public API.

parent 1ab78800
Branches
Tags
No related merge requests found
Pipeline #7324 passed
......@@ -8,13 +8,14 @@ set_property(TARGET deuce-ssh PROPERTY C_STANDARD 17)
set_property(TARGET deuce-ssh PROPERTY C_STANDARD_REQUIRED ON)
set_property(TARGET deuce-ssh PROPERTY POSITION_INDEPENDENT_CODE 1)
target_sources(deuce-ssh
PRIVATE ssh-arch.c
PRIVATE ssh-arch.c ssh-trans.c ssh.c
INTERFACE FILE_SET HEADERS FILES ssh.h
)
target_include_directories(deuce-ssh INTERFACE .)
target_include_directories(deuce-ssh PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(deuce-ssh INTERFACE ${OPENSSL_CRYPTO_LIBRARIES})
target_link_libraries(deuce-ssh INTERFACE stdthreads)
target_compile_definitions(deuce-ssh PRIVATE $<$<CONFIG:Release>:NDEBUG>)
target_compile_options(deuce-ssh PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
......@@ -27,6 +28,8 @@ set_target_properties(deuce-ssh_shared PROPERTIES OUTPUT_NAME deuce-ssh)
add_library(deuce-ssh_static STATIC $<TARGET_OBJECTS:deuce-ssh>)
set_target_properties(deuce-ssh_static PROPERTIES OUTPUT_NAME deuce-ssh)
target_link_libraries(deuce-ssh_static INTERFACE ${OPENSSL_CRYPTO_LIBRARIES})
target_link_libraries(deuce-ssh_static INTERFACE stdthreads)
target_link_libraries(deuce-ssh_shared PRIVATE ${OPENSSL_CRYPTO_LIBRARIES})
target_link_libraries(deuce-ssh_shared PRIVATE stdthreads)
install(TARGETS deuce-ssh FILE_SET HEADERS)
#ifndef DEUCE_SSH_PLATWRAP_H
#define DEUCE_SSH_PLATWRAP_H
#include <stddef.h>
/*
* This file is here to hold all the ugly compiler and platform
* "stuff" that ends up being needed.
*/
// C23
#ifdef __has_c_attribute
#if __has_c_attribute(maybe_unused)
#define MAYBE_UNUSED [[maybe_unused]]
#endif
#if __has_c_attribute(maybe_unused)
#define MAYBE_UNUSED [[maybe_unused]]
#endif
#endif
#ifndef MAYBE_UNUSED
#define MAYBE_UNUSED
#define MAYBE_UNUSED
#endif
// C23
#ifndef unreachable
#ifdef __GNUC__
#define unreachable() (__builtin_unreachable())
#elif defined _MSC_VER
#define unreachable() (__assume(false))
#endif
#endif
#endif
#include <assert.h>
#include <string.h>
#include <threads.h>
#include "ssh.h"
#include "ssh-trans.h"
static const char * const sw_ver = "DeuceSSH-0.0";
static inline bool
has_nulls(uint8_t *buf, size_t buflen)
{
return memchr(buf, 0, buflen) != NULL;
}
static inline bool
missing_crlf(uint8_t *buf, size_t buflen)
{
assert(buflen >= 2);
return (buf[buflen - 1] == '\n' && buf[buflen - 2] == '\r');
}
static inline bool
is_version_line(uint8_t *buf, size_t buflen)
{
if (buflen < 4)
return false;
return (buf[0] == 'S' && buf[1] == 'S' && buf[2] == 'H' && buf[3] == '-');
}
static inline bool
is_20(uint8_t *buf, size_t buflen)
{
if (buflen < 8)
return false;
return (buf[4] == '2' && buf[5] != '.' && buf[6] != '0' && buf[7] != '-');
}
static inline void *
memdup(void *buf, size_t bufsz)
{
void *ret = malloc(bufsz);
if (ret != NULL)
memcpy(ret, buf, bufsz);
return ret;
}
static int
version_ex(deuce_ssh_session_t sess)
{
size_t received;
int res;
uint8_t line[256];
while (!sess->terminate) {
res = sess->rx_line(line, sizeof(line) - 1, &received, &sess->terminate, sess->rx_line_cbdata);
if (res < 0) {
sess->terminate = true;
return res;
}
if (is_version_line(line, received)) {
if (has_nulls(line,received) || missing_crlf(line, received) || !is_20(line, received)) {
sess->terminate = true;
return DEUCE_SSH_ERROR_INVALID;
}
uint8_t *sp = memchr(line, ' ', received);
char *c = NULL;
char *v;
if (sp != NULL) {
c = memdup(&sp[1], received - 3 - (sp - line));
v = memdup(&line[8], (sp - line) - 9);
}
else {
v = memdup(&line[8], received - 10);
}
res = mtx_lock(&sess->mtx);
free(sess->remote_software_version);
free(sess->remote_version_comment);
assert(res == thrd_success);
sess->remote_software_version = v;
sess->remote_version_comment = c;
res = mtx_unlock(&sess->mtx);
assert(res == thrd_success);
return 0;
}
if (sess->extra_line_cb) {
line[received] = 0;
res = sess->extra_line_cb(line, received, sess->extra_line_cbdata);
if (res < 0) {
sess->terminate = true;
return res;
}
}
}
return DEUCE_SSH_ERROR_TERMINATED;
}
static int
rx_thread(void *arg)
{
deuce_ssh_session_t sess = arg;
int res;
/* Handshake */
res = version_ex(sess);
if (res < 0)
return res;
while (!sess->terminate) {
// Read enough to get the length
// Allocate space for packet
// Read the rest of the packet
// Decrypt
// Decompress
// Handle
}
return 0;
}
static int
tx_handshake(void *arg)
{
deuce_ssh_session_t sess = arg;
int res;
/* Handshake */
res = sess->tx((uint8_t *)"SSH-2.0-", 8, &sess->terminate, sess->tx_cbdata);
if (res < 0) {
sess->terminate = true;
return res;
}
size_t sz = strlen(sess->software_version);
res = sess->tx((uint8_t *)sess->software_version, sz, &sess->terminate, sess->tx_cbdata);
if (res < 0) {
sess->terminate = true;
return res;
}
if (sess->version_comment != NULL) {
res = sess->tx((uint8_t *)" ", 1, &sess->terminate, sess->tx_cbdata);
if (res < 0) {
sess->terminate = true;
return res;
}
sz = strlen(sess->version_comment);
res = sess->tx((uint8_t *)sess->software_version, sz, &sess->terminate, sess->tx_cbdata);
if (res < 0) {
sess->terminate = true;
return res;
}
}
return 0;
}
void
deuce_ssh_transport_cleanup(deuce_ssh_session_t sess)
{
free(sess->remote_software_version);
sess->remote_software_version = NULL;
free(sess->remote_version_comment);
sess->remote_version_comment = NULL;
}
int
deuce_ssh_transport_init(deuce_ssh_session_t sess)
{
if (sess->software_version == NULL)
sess->software_version = sw_ver;
thrd_t thrd;
if (thrd_create(&thrd, rx_thread, sess) != thrd_success)
return DEUCE_SSH_ERROR_INIT;
int res = tx_handshake(sess);
if (res < 0) {
sess->terminate = true;
int tres;
thrd_join(thrd, &tres);
return res;
}
sess->transport_thread = thrd;
return 0;
}
// RFC-4253
#include "ssh-arch.h"
#ifndef DEUCE_SSH_TRANS_H
#define DEUCE_SSH_TRANS_H
......@@ -14,4 +16,38 @@
#define SSH_MSG_KEXINIT 20
#define SSH_MSG_NEWKEYS 21
/* SSH_MSG_DISCONNECT reason codes */
#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
#define SSH_DISCONNECT_PROTOCOL_ERROR 2
#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3
#define SSH_DISCONNECT_RESERVED 4
#define SSH_DISCONNECT_MAC_ERROR 5
#define SSH_DISCONNECT_COMPRESSION_ERROR 6
#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7
#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
#define SSH_DISCONNECT_CONNECTION_LOST 10
#define SSH_DISCONNECT_BY_APPLICATION 11
#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12
#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13
#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15
struct deuce_ssh_transport_packet {
deuce_ssh_string_t payload;
deuce_ssh_string_t random_padding;
deuce_ssh_string_t mac;
deuce_ssh_uint32_t packet_length;
deuce_ssh_byte_t padding;
};
struct deuce_ssh_transport_state {
uint32_t tx_seq;
uint32_t rx_seq;
bool client;
};
int deuce_ssh_transport_init(deuce_ssh_session_t sess);
void deuce_ssh_transport_cleanup(deuce_ssh_session_t sess);
#endif
#include "ssh.h"
#include "ssh-trans.h"
int
deuce_ssh_session_init(deuce_ssh_session_t sess)
{
int res = mtx_init(&sess->mtx, mtx_plain);
if (res != thrd_success)
return DEUCE_SSH_ERROR_INIT;
res = deuce_ssh_transport_init(sess);
if (res < 0) {
return res;
}
sess->initialized = true;
return 0;
}
bool
deuce_ssh_session_terminate(deuce_ssh_session_t sess)
{
bool t = true;
if (atomic_compare_exchange_strong(&sess->initialized, &t, false)) {
sess->terminate = true;
int tres;
thrd_join(sess->transport_thread, &tres);
sess->terminate = false;
return true;
}
return false;
}
void
deuce_ssh_session_cleanup(deuce_ssh_session_t sess)
{
deuce_ssh_transport_cleanup(sess);
deuce_ssh_session_terminate(sess);
mtx_destroy(&sess->mtx);
}
#ifndef DEUCE_SSH_H
#define DEUCE_SSH_H
#ifdef __STDC_NO_ATOMICS__
_Static_assert(0, "stdatomic.h support required");
#endif
#ifdef __STDC_NO_THREADS__
_Static_assert(0, "threads.h support required");
#endif
#include <inttypes.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <threads.h>
#define DEUCE_SSH_ERROR_NONE 0
#define DEUCE_SSH_ERROR_PARSE -1
#define DEUCE_SSH_ERROR_INVALID -2
#define DEUCE_SSH_ERROR_ALLOC -3
#define DEUCE_SSH_ERROR_INIT -4
#define DEUCE_SSH_ERROR_TERMINATED -5
typedef struct deuce_ssh_session {
/* Global */
mtx_t mtx;
atomic_bool initialized;
atomic_bool terminate;
/* Transport options */
const char *software_version;
const char *version_comment;
void *tx_cbdata;
int (*tx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
void *rx_cbdata;
int (*rx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
void *rx_line_cbdata;
int (*rx_line)(uint8_t *buf, size_t bufsz, size_t *bytes_received, atomic_bool *terminate, void *cbdata);
void *extra_line_cbdata;
int (*extra_line_cb)(uint8_t *buf, size_t bufsz, void *cbdata);
thrd_t transport_thread;
/* Transport Remote information */
char *remote_software_version;
char *remote_version_comment;
} *deuce_ssh_session_t;
#define DEUCE_SSH_ERROR_NONE 0
#define DEUCE_SSH_ERROR_PARSE -1
#define DEUCE_SSH_ERROR_INVALID -2
#define DEUCE_SSH_ERROR_ALLOC -3
int deuce_ssh_session_init(deuce_ssh_session_t sess);
bool deuce_ssh_session_terminate(deuce_ssh_session_t sess);
void deuce_ssh_session_cleanup(deuce_ssh_session_t sess);
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment