diff --git a/src/ssh/ssh-trans.c b/src/ssh/ssh-trans.c index 5fa1ab01a2e60f9f94b1b3b251adc463f5d340a0..7f5ccb0a5eeca818dfb7f865a545c4263d817112 100644 --- a/src/ssh/ssh-trans.c +++ b/src/ssh/ssh-trans.c @@ -5,7 +5,22 @@ #include "ssh.h" #include "ssh-trans.h" +typedef struct deuce_ssh_transport_global_config { + atomic_bool used; + const char *software_version; + const char *version_comment; + size_t kex_entries; + char **kex_name; + deuce_ssh_kex_handler_t *kex_handler; + deuce_ssh_kex_cleanup_t *kex_cleanup; + int (*tx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata); + int (*rx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata); + int (*rx_line)(uint8_t *buf, size_t bufsz, size_t *bytes_received, atomic_bool *terminate, void *cbdata); + int (*extra_line_cb)(uint8_t *buf, size_t bufsz, void *cbdata); +} *deuce_ssh_transport_global_config_t; + static const char * const sw_ver = "DeuceSSH-0.0"; +static struct deuce_ssh_transport_global_config gconf; static inline bool has_nulls(uint8_t *buf, size_t buflen) @@ -53,7 +68,7 @@ version_ex(deuce_ssh_session_t sess) uint8_t line[256]; while (!sess->terminate) { - res = sess->rx_line(line, sizeof(line) - 1, &received, &sess->terminate, sess->rx_line_cbdata); + res = gconf.rx_line(line, sizeof(line) - 1, &received, &sess->terminate, sess->rx_line_cbdata); if (res < 0) { sess->terminate = true; return res; @@ -83,9 +98,9 @@ version_ex(deuce_ssh_session_t sess) assert(res == thrd_success); return 0; } - if (sess->extra_line_cb) { + if (gconf.extra_line_cb) { line[received] = 0; - res = sess->extra_line_cb(line, received, sess->extra_line_cbdata); + res = gconf.extra_line_cb(line, received, sess->extra_line_cbdata); if (res < 0) { sess->terminate = true; return res; @@ -122,38 +137,38 @@ tx_handshake(void *arg) { deuce_ssh_session_t sess = arg; int res; + uint8_t line[255]; + size_t sz = 0; /* Handshake */ - res = sess->tx((uint8_t *)"SSH-2.0-", 8, &sess->terminate, sess->tx_cbdata); - if (res < 0) { - sess->terminate = true; - return res; + memcpy(line, "SSH-2.0-", 8); + sz += 8; + size_t asz = strlen(gconf.software_version); + memcpy(&line[sz], gconf.software_version, asz); + sz += asz; + if (gconf.version_comment != NULL) { + memcpy(&line[sz], " ", 1); + sz += 1; + asz = strlen(gconf.version_comment); + memcpy(&line[sz], gconf.version_comment, asz); + sz += asz; } - size_t sz = strlen(sess->software_version); - res = sess->tx((uint8_t *)sess->software_version, sz, &sess->terminate, sess->tx_cbdata); + res = gconf.tx(line, 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) { + if (sess->trans->kex_selected != SIZE_MAX) { + if (gconf.kex_cleanup[sess->trans->kex_selected] != NULL) + gconf.kex_cleanup[sess->trans->kex_selected](sess); + sess->trans->kex_selected = SIZE_MAX; + } free(sess->remote_software_version); sess->remote_software_version = NULL; free(sess->remote_version_comment); @@ -163,9 +178,13 @@ deuce_ssh_transport_cleanup(deuce_ssh_session_t sess) int deuce_ssh_transport_init(deuce_ssh_session_t sess) { - if (sess->software_version == NULL) - sess->software_version = sw_ver; + gconf.used = true; + if (gconf.software_version == NULL) + gconf.software_version = sw_ver; + sess->trans = calloc(1, sizeof(struct deuce_ssh_transport_state)); + if (sess->trans == NULL) + return DEUCE_SSH_ERROR_ALLOC; thrd_t thrd; if (thrd_create(&thrd, rx_thread, sess) != thrd_success) return DEUCE_SSH_ERROR_INIT; @@ -176,7 +195,55 @@ deuce_ssh_transport_init(deuce_ssh_session_t sess) thrd_join(thrd, &tres); return res; } + sess->trans->kex_selected = SIZE_MAX; + sess->trans->kex_state = 0; + + sess->trans->transport_thread = thrd; + return 0; +} + +int +deuce_ssh_transport_register_kex(const char *name, deuce_ssh_kex_handler_t kex_handler, deuce_ssh_kex_cleanup_t kex_cleanup) +{ + if (gconf.used) + return DEUCE_SSH_ERROR_TOOLATE; + if (gconf.kex_entries + 1 == SIZE_MAX) + return DEUCE_SSH_ERROR_TOOMANY; + char **newnames = realloc(gconf.kex_name, sizeof(gconf.kex_name[0]) * (gconf.kex_entries + 1)); + if (newnames == NULL) + return DEUCE_SSH_ERROR_ALLOC; + gconf.kex_name = newnames; + gconf.kex_name[gconf.kex_entries] = strdup(name); + + deuce_ssh_kex_handler_t *newhandlers = realloc(gconf.kex_handler, sizeof(gconf.kex_handler[0]) * (gconf.kex_entries + 1)); + if (newhandlers == NULL) { + free(gconf.kex_name[gconf.kex_entries]); + return DEUCE_SSH_ERROR_ALLOC; + } + gconf.kex_handler = newhandlers; + gconf.kex_handler[gconf.kex_entries] = kex_handler; + + deuce_ssh_kex_cleanup_t *newcleanup = realloc(gconf.kex_cleanup, sizeof(gconf.kex_cleanup[0]) * (gconf.kex_entries + 1)); + if (newcleanup == NULL) { + free(gconf.kex_name[gconf.kex_entries]); + return DEUCE_SSH_ERROR_ALLOC; + } + gconf.kex_cleanup = newcleanup; + gconf.kex_cleanup[gconf.kex_entries] = kex_cleanup; + + gconf.kex_entries++; + return 0; +} + +int +deuce_ssh_transport_set_callbacks(deuce_ssh_transport_io_cb_t tx, deuce_ssh_transport_io_cb_t rx, deuce_ssh_transport_rxline_cb_t rx_line, deuce_ssh_transport_extra_line_cb_t extra_line_cb) +{ + if (gconf.used) + return DEUCE_SSH_ERROR_TOOLATE; + gconf.tx = tx; + gconf.rx = rx; + gconf.rx_line = rx_line; + gconf.extra_line_cb = extra_line_cb; - sess->transport_thread = thrd; return 0; } diff --git a/src/ssh/ssh-trans.h b/src/ssh/ssh-trans.h index 11e68f23377db9920c204d7975f1623451af4e93..f388531ed0b2f34b95c78f30a186b6c244d7e811 100644 --- a/src/ssh/ssh-trans.h +++ b/src/ssh/ssh-trans.h @@ -1,5 +1,6 @@ // RFC-4253 +#include "ssh.h" #include "ssh-arch.h" #ifndef DEUCE_SSH_TRANS_H @@ -33,19 +34,41 @@ #define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 #define SSH_DISCONNECT_ILLEGAL_USER_NAME 15 -struct deuce_ssh_transport_packet { +typedef 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; -}; +} *deuce_ssh_transport_packet_t; -struct deuce_ssh_transport_state { +typedef int (*deuce_ssh_kex_handler_t)(deuce_ssh_transport_packet_t pkt, deuce_ssh_session_t sess); +typedef void (*deuce_ssh_kex_cleanup_t)(deuce_ssh_session_t sess); + +typedef struct deuce_ssh_transport_state { uint32_t tx_seq; uint32_t rx_seq; bool client; -}; + + /* Transport options */ + thrd_t transport_thread; + + /* KEX options */ + void *kex_cbdata; + uint32_t kex_state; + size_t kex_selected; + + /* KEX outputs */ + size_t shared_secret_sz; + uint8_t *shared_secret; + size_t exchange_hash_sz; + uint8_t *exchange_hash; + + size_t enc_selected; + size_t mac_selected; + size_t comp_selected; + +} *deuce_ssh_transport_state_t; int deuce_ssh_transport_init(deuce_ssh_session_t sess); void deuce_ssh_transport_cleanup(deuce_ssh_session_t sess); diff --git a/src/ssh/ssh.c b/src/ssh/ssh.c index 4b645ee0de20dd4ef3306a616ca8bbc77bf6ec25..c688776e9a64e1926a0923c92c3d29c3669459c8 100644 --- a/src/ssh/ssh.c +++ b/src/ssh/ssh.c @@ -9,9 +9,8 @@ deuce_ssh_session_init(deuce_ssh_session_t sess) return DEUCE_SSH_ERROR_INIT; res = deuce_ssh_transport_init(sess); - if (res < 0) { + if (res < 0) return res; - } sess->initialized = true; return 0; @@ -24,7 +23,7 @@ deuce_ssh_session_terminate(deuce_ssh_session_t sess) if (atomic_compare_exchange_strong(&sess->initialized, &t, false)) { sess->terminate = true; int tres; - thrd_join(sess->transport_thread, &tres); + thrd_join(sess->trans->transport_thread, &tres); sess->terminate = false; return true; } diff --git a/src/ssh/ssh.h b/src/ssh/ssh.h index 7036cbad02f0d7aa629115361a9605962b7783f9..aef1214f4be92d1e9698d631380a5d4ee72f75ba 100644 --- a/src/ssh/ssh.h +++ b/src/ssh/ssh.h @@ -20,6 +20,13 @@ _Static_assert(0, "threads.h support required"); #define DEUCE_SSH_ERROR_ALLOC -3 #define DEUCE_SSH_ERROR_INIT -4 #define DEUCE_SSH_ERROR_TERMINATED -5 +#define DEUCE_SSH_ERROR_TOOLATE -6 +#define DEUCE_SSH_ERROR_TOOMANY -7 + +typedef struct deuce_ssh_transport_state *deuce_ssh_transport_state_t; +typedef int (*deuce_ssh_transport_io_cb_t)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata); +typedef int (*deuce_ssh_transport_rxline_cb_t)(uint8_t *buf, size_t bufsz, size_t *bytes_received, atomic_bool *terminate, void *cbdata); +typedef int (*deuce_ssh_transport_extra_line_cb_t)(uint8_t *buf, size_t bufsz, void *cbdata); typedef struct deuce_ssh_session { /* Global */ @@ -27,22 +34,16 @@ typedef struct deuce_ssh_session { atomic_bool initialized; atomic_bool terminate; - /* Transport options */ - const char *software_version; - const char *version_comment; + /* Transport Remote information */ + char *remote_software_version; + char *remote_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_transport_state_t trans; } *deuce_ssh_session_t; int deuce_ssh_session_init(deuce_ssh_session_t sess);