mirror of
https://github.com/tnn2/esp-idf-libssh.git
synced 2026-02-04 12:20:42 +09:00
add sample project
This commit is contained in:
4
examples/sshd/CMakeLists.txt
Normal file
4
examples/sshd/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
set(EXTRA_COMPONENT_DIRS ../../components/libssh)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(sshd)
|
||||
6
examples/sshd/main/CMakeLists.txt
Normal file
6
examples/sshd/main/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
idf_component_register(SRCS "wifi.c"
|
||||
"main.c"
|
||||
"sshd.c"
|
||||
"sshd_task.c"
|
||||
"minicli.c"
|
||||
INCLUDE_DIRS ".")
|
||||
13
examples/sshd/main/example.h
Normal file
13
examples/sshd/main/example.h
Normal file
@@ -0,0 +1,13 @@
|
||||
void initialize_wifi(void);
|
||||
void wifi_sta_join(const char* ssid, const char* pass);
|
||||
void start_sshd(void);
|
||||
|
||||
struct interactive_session {
|
||||
void (*is_handle_char_from_remote)(struct interactive_session *, char);
|
||||
void (*is_handle_char_from_local)(struct interactive_session *, char);
|
||||
void *is_data;
|
||||
};
|
||||
|
||||
void minicli_handle_command(struct interactive_session *, const char *);
|
||||
void minicli_begin_interactive_session(struct interactive_session *);
|
||||
|
||||
25
examples/sshd/main/main.c
Normal file
25
examples/sshd/main/main.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "example.h"
|
||||
|
||||
static void
|
||||
initialize_nvs()
|
||||
{
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK( nvs_flash_erase() );
|
||||
err = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
initialize_nvs();
|
||||
initialize_wifi();
|
||||
/* replace with SSID and passphrase */
|
||||
wifi_sta_join("mywifi", "supasecret");
|
||||
start_sshd();
|
||||
}
|
||||
|
||||
142
examples/sshd/main/minicli.c
Normal file
142
examples/sshd/main/minicli.c
Normal file
@@ -0,0 +1,142 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "example.h"
|
||||
|
||||
char next_command[16];
|
||||
uint8_t next_command_idx = 0;
|
||||
|
||||
static void minicli_command_banner(struct interactive_session *);
|
||||
static void minicli_command_help(struct interactive_session *);
|
||||
static void minicli_command_noop(struct interactive_session *);
|
||||
|
||||
struct minicli_command {
|
||||
char *cmd;
|
||||
void (*handler)(struct interactive_session *);
|
||||
};
|
||||
|
||||
struct minicli_command minicli_commands[] = {
|
||||
{ "banner", minicli_command_banner},
|
||||
{ "help", minicli_command_help},
|
||||
{ "", minicli_command_noop},
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
void
|
||||
minicli_printf(struct interactive_session *is, const char *fmt, ...)
|
||||
{
|
||||
char tmp[512];
|
||||
int i;
|
||||
va_list args;
|
||||
|
||||
va_start (args, fmt);
|
||||
vsnprintf(tmp, sizeof(tmp), fmt, args);
|
||||
va_end (args);
|
||||
for (i = 0; i < sizeof(tmp); i++) {
|
||||
if (tmp[i] == 0) {
|
||||
return;
|
||||
} else {
|
||||
if (tmp[i] == '\n')
|
||||
is->is_handle_char_from_local(is, '\r');
|
||||
is->is_handle_char_from_local(is, tmp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
minicli_command_noop(struct interactive_session *is)
|
||||
{
|
||||
}
|
||||
|
||||
static const char banner[] = "\n"
|
||||
" _ _ _ _ __ __ _ _\n"
|
||||
"| || |___| | |___ \\ \\ / /__ _ _| |__| |\n"
|
||||
"| __ / -_) | / _ \\_ \\ \\/\\/ / _ \\ '_| / _` |\n"
|
||||
"|_||_\\___|_|_\\___( ) \\_/\\_/\\___/_| |_\\__,_|\n"
|
||||
" |/\n"
|
||||
"Welcome to minicli! Type ^D to exit and 'help' for help.\n";
|
||||
|
||||
|
||||
static void
|
||||
minicli_command_banner(struct interactive_session *is)
|
||||
{
|
||||
minicli_printf(is, banner);
|
||||
}
|
||||
|
||||
static void
|
||||
minicli_command_help(struct interactive_session *is)
|
||||
{
|
||||
struct minicli_command *cc;
|
||||
|
||||
cc = minicli_commands;
|
||||
while (cc->cmd != NULL) {
|
||||
minicli_printf(is, " %s\n", cc->cmd);
|
||||
cc++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
minicli_prompt(struct interactive_session *is)
|
||||
{
|
||||
minicli_printf(is, "minicli> ");
|
||||
}
|
||||
|
||||
void
|
||||
minicli_handle_command(struct interactive_session *is, const char *cmd)
|
||||
{
|
||||
struct minicli_command *cc;
|
||||
|
||||
cc = minicli_commands;
|
||||
while (cc->cmd != NULL) {
|
||||
if (!strcmp(cmd, cc->cmd)) {
|
||||
cc->handler(is);
|
||||
minicli_prompt(is);
|
||||
return;
|
||||
}
|
||||
cc++;
|
||||
}
|
||||
minicli_printf(is, "%c? unknown command\n", 7);
|
||||
minicli_prompt(is);
|
||||
}
|
||||
|
||||
static void
|
||||
minicli_handle_char(struct interactive_session *is, char c)
|
||||
{
|
||||
if (c == '\r') {
|
||||
minicli_printf(is, "\n");
|
||||
next_command[next_command_idx] = 0;
|
||||
minicli_handle_command(is, next_command);
|
||||
next_command_idx = 0;
|
||||
} else if (c == 3) {
|
||||
// ^C
|
||||
minicli_printf(is, "^C\n");
|
||||
next_command[next_command_idx] = 0;
|
||||
minicli_prompt(is);
|
||||
} else if (c == 4) {
|
||||
// ^D
|
||||
} else if (c == 127) {
|
||||
// backspace
|
||||
if (next_command_idx > 0) {
|
||||
minicli_printf(is, "%c %c", 8, 8);
|
||||
next_command_idx--;
|
||||
} else {
|
||||
minicli_printf(is, "%c", 7);
|
||||
}
|
||||
} else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
|
||||
if (next_command_idx < sizeof(next_command) - 1) {
|
||||
minicli_printf(is, "%c", c);
|
||||
next_command[next_command_idx++] = c;
|
||||
}
|
||||
} else {
|
||||
// invalid chars ignored
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
minicli_begin_interactive_session(struct interactive_session *is)
|
||||
{
|
||||
is->is_handle_char_from_remote = minicli_handle_char;
|
||||
minicli_command_banner(is);
|
||||
minicli_prompt(is);
|
||||
}
|
||||
431
examples/sshd/main/sshd.c
Normal file
431
examples/sshd/main/sshd.c
Normal file
@@ -0,0 +1,431 @@
|
||||
#include <stdio.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/server.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stddef.h>
|
||||
#include "example.h"
|
||||
#include "sshd.h"
|
||||
|
||||
static void handle_char_from_local(struct interactive_session *, char);
|
||||
|
||||
/*
|
||||
* XXX protos for internal libssh APIs.
|
||||
* needed for non-blocking handling of listening socket.
|
||||
*/
|
||||
struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct *);
|
||||
int ssh_event_add_poll(ssh_event event, struct ssh_poll_handle_struct *);
|
||||
int ssh_event_remove_poll(ssh_event event, struct ssh_poll_handle_struct *);
|
||||
|
||||
/*
|
||||
* Heavy API abuse here. But upstream doesn't provide a
|
||||
* native mechanism for loading host keys from memory.
|
||||
* We assume fields in "struct ssh_bind_struct" are laid
|
||||
* out in exactly this order:
|
||||
* ssh_key ecdsa;
|
||||
* ssh_key dsa;
|
||||
* ssh_key rsa;
|
||||
* ssh_key ed25519;
|
||||
* char *bindaddr;
|
||||
*/
|
||||
static int
|
||||
import_embedded_host_key(ssh_bind sshbind, const char *base64_key)
|
||||
{
|
||||
size_t ptralign = sizeof(void*);
|
||||
char buf[2048];
|
||||
char *p, *q, *e;
|
||||
ssh_key *target;
|
||||
int error;
|
||||
ssh_key probe;
|
||||
enum ssh_keytypes_e type;
|
||||
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, "");
|
||||
memcpy(buf, sshbind, sizeof(buf));
|
||||
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR,
|
||||
"0123456789ABCDEF0123456789ABCDEF");
|
||||
p = buf;
|
||||
e = p + sizeof(buf);
|
||||
q = (char*)sshbind;
|
||||
while (p < e) {
|
||||
if (memcmp(p, q, ptralign) != 0)
|
||||
break;
|
||||
p += ptralign;
|
||||
q += ptralign;
|
||||
}
|
||||
if (p >= e)
|
||||
return SSH_ERROR;
|
||||
probe = ssh_key_new();
|
||||
if (probe == NULL)
|
||||
return SSH_ERROR;
|
||||
error = ssh_pki_import_privkey_base64(base64_key, NULL, NULL, NULL,
|
||||
&probe);
|
||||
type = ssh_key_type(probe);
|
||||
ssh_key_free(probe);
|
||||
if (error != SSH_OK)
|
||||
return error;
|
||||
switch (type) {
|
||||
case SSH_KEYTYPE_ECDSA_P256:
|
||||
case SSH_KEYTYPE_ECDSA_P521:
|
||||
target = (ssh_key*)((uintptr_t)sshbind + (p - buf)
|
||||
- 4 * ptralign);
|
||||
break;
|
||||
case SSH_KEYTYPE_DSS:
|
||||
target = (ssh_key*)((uintptr_t)sshbind + (p - buf)
|
||||
- 3 * ptralign);
|
||||
break;
|
||||
case SSH_KEYTYPE_RSA:
|
||||
target = (ssh_key*)((uintptr_t)sshbind + (p - buf)
|
||||
- 2 * ptralign);
|
||||
break;
|
||||
case SSH_KEYTYPE_ED25519:
|
||||
target = (ssh_key*)((uintptr_t)sshbind + (p - buf)
|
||||
- 1 * ptralign);
|
||||
break;
|
||||
default:
|
||||
return SSH_ERROR;
|
||||
}
|
||||
error = ssh_pki_import_privkey_base64(base64_key, NULL, NULL, NULL,
|
||||
target);
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct client_ctx *
|
||||
lookup_client(struct server_ctx *sc, ssh_session session)
|
||||
{
|
||||
struct client_ctx *ret;
|
||||
|
||||
SLIST_FOREACH(ret, &sc->sc_client_head, cc_client_list) {
|
||||
if (ret->cc_session == session)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
auth_password(ssh_session session, const char *user, const char *password,
|
||||
void *userdata)
|
||||
{
|
||||
struct server_ctx *sc = (struct server_ctx *)userdata;
|
||||
struct client_ctx *cc;
|
||||
struct ssh_user *su;
|
||||
|
||||
cc = lookup_client(sc, session);
|
||||
if (cc == NULL)
|
||||
return SSH_AUTH_DENIED;
|
||||
if (cc->cc_didauth)
|
||||
return SSH_AUTH_DENIED;
|
||||
su = sc->sc_lookup_user(sc, user);
|
||||
if (su == NULL)
|
||||
return SSH_AUTH_DENIED;
|
||||
if (strcmp(password, su->su_password) != 0)
|
||||
return SSH_AUTH_DENIED;
|
||||
cc->cc_didauth = true;
|
||||
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
auth_publickey(ssh_session session, const char *user,
|
||||
struct ssh_key_struct *pubkey, char signature_state, void *userdata)
|
||||
{
|
||||
struct server_ctx *sc = (struct server_ctx *) userdata;
|
||||
struct client_ctx *cc;
|
||||
struct ssh_user *su;
|
||||
ssh_key key;
|
||||
int error;
|
||||
|
||||
cc = lookup_client(sc, session);
|
||||
if (cc == NULL)
|
||||
return SSH_AUTH_DENIED;
|
||||
if (signature_state == SSH_PUBLICKEY_STATE_NONE)
|
||||
return SSH_AUTH_SUCCESS;
|
||||
if (signature_state != SSH_PUBLICKEY_STATE_VALID)
|
||||
return SSH_AUTH_DENIED;
|
||||
if (cc->cc_didauth)
|
||||
return SSH_AUTH_DENIED;
|
||||
su = sc->sc_lookup_user(sc, user);
|
||||
if (su == NULL)
|
||||
return SSH_AUTH_DENIED;
|
||||
if (su->su_base64_key == NULL)
|
||||
return SSH_AUTH_DENIED;
|
||||
if (ssh_pki_import_pubkey_base64(su->su_base64_key, su->su_keytype,
|
||||
&key) != SSH_OK)
|
||||
return SSH_AUTH_DENIED;
|
||||
error = ssh_key_cmp(key, pubkey, SSH_KEY_CMP_PUBLIC);
|
||||
ssh_key_free(key);
|
||||
if (error != SSH_OK)
|
||||
return SSH_AUTH_DENIED;
|
||||
cc->cc_didauth = true;
|
||||
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
data_function(ssh_session session, ssh_channel channel, void *data, uint32_t len,
|
||||
int is_stderr, void *userdata)
|
||||
{
|
||||
struct client_ctx *cc = (struct client_ctx *)userdata;
|
||||
int i;
|
||||
char c;
|
||||
for (i = 0; i < len; i++) {
|
||||
c = ((char*)data)[i];
|
||||
if (c == 0x4) /* ^D */ {
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
return len;
|
||||
}
|
||||
cc->cc_is.is_handle_char_from_remote(&cc->cc_is, c);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
pty_request(ssh_session session, ssh_channel channel, const char *term,
|
||||
int cols, int rows, int py, int px, void *userdata) {
|
||||
struct client_ctx *cc = (struct client_ctx *)userdata;
|
||||
|
||||
if (cc->cc_didpty)
|
||||
return SSH_ERROR;
|
||||
cc->cc_cols = cols;
|
||||
cc->cc_rows = rows;
|
||||
cc->cc_px = px;
|
||||
cc->cc_py = py;
|
||||
strlcpy(cc->cc_term, term, sizeof(cc->cc_term));
|
||||
cc->cc_didpty = true;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
shell_request(ssh_session session, ssh_channel channel, void *userdata)
|
||||
{
|
||||
struct client_ctx *cc = (struct client_ctx *)userdata;
|
||||
if (cc->cc_didshell)
|
||||
return SSH_ERROR;
|
||||
cc->cc_didshell = true;
|
||||
cc->cc_is.is_handle_char_from_local = handle_char_from_local;
|
||||
cc->cc_begin_interactive_session(&cc->cc_is);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static int exec_request(ssh_session session, ssh_channel channel,
|
||||
const char *command, void *userdata)
|
||||
{
|
||||
struct client_ctx *cc = (struct client_ctx *)userdata;
|
||||
if (cc->cc_didshell)
|
||||
return SSH_ERROR;
|
||||
cc->cc_is.is_handle_char_from_local = handle_char_from_local;
|
||||
minicli_handle_command(&cc->cc_is, command);
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
pty_resize(ssh_session session, ssh_channel channel, int cols,
|
||||
int rows, int py, int px, void *userdata)
|
||||
{
|
||||
struct client_ctx *cc = (struct client_ctx *)userdata;
|
||||
|
||||
cc->cc_cols = cols;
|
||||
cc->cc_rows = rows;
|
||||
cc->cc_px = px;
|
||||
cc->cc_py = py;
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static ssh_channel
|
||||
channel_open(ssh_session session, void *userdata)
|
||||
{
|
||||
struct server_ctx *sc = (struct server_ctx *)userdata;
|
||||
struct client_ctx *cc;
|
||||
|
||||
cc = lookup_client(sc, session);
|
||||
if (cc == NULL)
|
||||
return NULL;
|
||||
if (cc->cc_didchannel)
|
||||
return NULL;
|
||||
cc->channel_cb = (struct ssh_channel_callbacks_struct) {
|
||||
.channel_data_function = data_function,
|
||||
.channel_exec_request_function = exec_request,
|
||||
.channel_pty_request_function = pty_request,
|
||||
.channel_pty_window_change_function = pty_resize,
|
||||
.channel_shell_request_function = shell_request,
|
||||
.userdata = cc
|
||||
};
|
||||
cc->cc_channel = ssh_channel_new(session);
|
||||
ssh_callbacks_init(&cc->channel_cb);
|
||||
ssh_set_channel_callbacks(cc->cc_channel, &cc->channel_cb);
|
||||
cc->cc_didchannel = true;
|
||||
|
||||
return cc->cc_channel;
|
||||
}
|
||||
|
||||
static void
|
||||
incoming_connection(ssh_bind sshbind, void *userdata)
|
||||
{
|
||||
struct server_ctx *sc = (struct server_ctx *)userdata;
|
||||
long t = 0;
|
||||
struct client_ctx *cc = calloc(1, sizeof(struct client_ctx));
|
||||
|
||||
cc->cc_session = ssh_new();
|
||||
if (ssh_bind_accept(sshbind, cc->cc_session) == SSH_ERROR) {
|
||||
goto cleanup;
|
||||
}
|
||||
cc->cc_begin_interactive_session = sc->sc_begin_interactive_session;
|
||||
ssh_set_callbacks(cc->cc_session, &sc->sc_generic_cb);
|
||||
ssh_set_server_callbacks(cc->cc_session, &sc->sc_server_cb);
|
||||
ssh_set_auth_methods(cc->cc_session, sc->sc_auth_methods);
|
||||
ssh_set_blocking(cc->cc_session, 0);
|
||||
(void) ssh_options_set(cc->cc_session, SSH_OPTIONS_TIMEOUT, &t);
|
||||
(void) ssh_options_set(cc->cc_session, SSH_OPTIONS_TIMEOUT_USEC, &t);
|
||||
|
||||
if (ssh_handle_key_exchange(cc->cc_session) == SSH_ERROR) {
|
||||
ssh_disconnect(cc->cc_session);
|
||||
goto cleanup;
|
||||
}
|
||||
/*
|
||||
* Since we set the socket to non-blocking already,
|
||||
* ssh_handle_key_exchange will return SSH_AGAIN.
|
||||
* Start polling the socket and let the main loop drive the kex.
|
||||
*/
|
||||
SLIST_INSERT_HEAD(&sc->sc_client_head, cc, cc_client_list);
|
||||
ssh_event_add_session(sc->sc_sshevent, cc->cc_session);
|
||||
return;
|
||||
cleanup:
|
||||
ssh_free(cc->cc_session);
|
||||
free(cc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleans up dead clients.
|
||||
*/
|
||||
static void
|
||||
dead_eater(struct server_ctx *sc)
|
||||
{
|
||||
struct client_ctx *cc;
|
||||
struct client_ctx *cc_removed = NULL;
|
||||
int status;
|
||||
|
||||
SLIST_FOREACH(cc, &sc->sc_client_head, cc_client_list) {
|
||||
if (cc_removed) {
|
||||
free(cc_removed);
|
||||
cc_removed = NULL;
|
||||
}
|
||||
status = ssh_get_status(cc->cc_session);
|
||||
if (status & (SSH_CLOSED | SSH_CLOSED_ERROR)) {
|
||||
if (cc->cc_didchannel) {
|
||||
ssh_channel_free(cc->cc_channel);
|
||||
}
|
||||
ssh_event_remove_session(sc->sc_sshevent, cc->cc_session);
|
||||
ssh_free(cc->cc_session);
|
||||
SLIST_REMOVE(&sc->sc_client_head, cc, client_ctx, cc_client_list);
|
||||
cc_removed = cc;
|
||||
}
|
||||
}
|
||||
if (cc_removed) {
|
||||
free(cc_removed);
|
||||
cc_removed = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
create_new_server(struct server_ctx *sc)
|
||||
{
|
||||
SLIST_INIT(&sc->sc_client_head);
|
||||
sc->sc_server_cb = (struct ssh_server_callbacks_struct){
|
||||
.userdata = sc,
|
||||
.auth_password_function = auth_password,
|
||||
.auth_pubkey_function = auth_publickey,
|
||||
.channel_open_request_session_function = channel_open
|
||||
};
|
||||
sc->sc_generic_cb = (struct ssh_callbacks_struct){
|
||||
.userdata = sc
|
||||
};
|
||||
sc->sc_bind_cb = (struct ssh_bind_callbacks_struct){
|
||||
.incoming_connection = incoming_connection
|
||||
};
|
||||
ssh_callbacks_init(&sc->sc_server_cb);
|
||||
ssh_callbacks_init(&sc->sc_generic_cb);
|
||||
ssh_callbacks_init(&sc->sc_bind_cb);
|
||||
sc->sc_sshbind = ssh_bind_new();
|
||||
if (sc->sc_sshbind == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_bind_options_set(sc->sc_sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "1");
|
||||
ssh_bind_set_callbacks(sc->sc_sshbind, &sc->sc_bind_cb, sc);
|
||||
import_embedded_host_key(sc->sc_sshbind, sc->sc_host_key);
|
||||
ssh_bind_options_set(sc->sc_sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, "22");
|
||||
ssh_bind_options_set(sc->sc_sshbind, SSH_BIND_OPTIONS_BINDADDR, "0.0.0.0");
|
||||
if(ssh_bind_listen(sc->sc_sshbind) < 0) {
|
||||
ssh_bind_free(sc->sc_sshbind);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
ssh_bind_set_blocking(sc->sc_sshbind, 0);
|
||||
ssh_event_add_poll(sc->sc_sshevent, ssh_bind_get_poll(sc->sc_sshbind));
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
terminate_server(struct server_ctx *sc)
|
||||
{
|
||||
struct client_ctx *cc;
|
||||
|
||||
ssh_event_remove_poll(sc->sc_sshevent, ssh_bind_get_poll(sc->sc_sshbind));
|
||||
close(ssh_bind_get_fd(sc->sc_sshbind));
|
||||
SLIST_FOREACH(cc, &sc->sc_client_head, cc_client_list) {
|
||||
ssh_silent_disconnect(cc->cc_session);
|
||||
}
|
||||
while (!SLIST_EMPTY(&sc->sc_client_head)) {
|
||||
(void) ssh_event_dopoll(sc->sc_sshevent, 100);
|
||||
dead_eater(sc);
|
||||
}
|
||||
ssh_bind_free(sc->sc_sshbind);
|
||||
free(sc);
|
||||
}
|
||||
|
||||
int
|
||||
sshd_main(struct server_ctx *sc)
|
||||
{
|
||||
int error;
|
||||
ssh_event event;
|
||||
static bool time_to_die = false;
|
||||
|
||||
if (ssh_init() < 0) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
event = ssh_event_new();
|
||||
if (!event)
|
||||
return SSH_ERROR;
|
||||
sc->sc_sshevent = event;
|
||||
if (create_new_server(sc) != SSH_OK)
|
||||
return SSH_ERROR;
|
||||
|
||||
while (!time_to_die) {
|
||||
error = ssh_event_dopoll(sc->sc_sshevent, 1000);
|
||||
if (error == SSH_ERROR || error == SSH_AGAIN) {
|
||||
/* check if any clients are dead and consume 'em */
|
||||
dead_eater(sc);
|
||||
}
|
||||
}
|
||||
terminate_server(sc);
|
||||
ssh_event_free(event);
|
||||
ssh_finalize();
|
||||
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_char_from_local(struct interactive_session *is, char c)
|
||||
{
|
||||
struct client_ctx *cc = (struct client_ctx *)((uintptr_t)is - offsetof(struct client_ctx, cc_is));
|
||||
ssh_channel_write(cc->cc_channel, &c, 1);
|
||||
}
|
||||
|
||||
|
||||
39
examples/sshd/main/sshd.h
Normal file
39
examples/sshd/main/sshd.h
Normal file
@@ -0,0 +1,39 @@
|
||||
struct ssh_user {
|
||||
const char *su_user;
|
||||
const char *su_password;
|
||||
const enum ssh_keytypes_e su_keytype;
|
||||
const char *su_base64_key;
|
||||
};
|
||||
|
||||
struct client_ctx {
|
||||
ssh_session cc_session;
|
||||
ssh_channel cc_channel;
|
||||
struct ssh_channel_callbacks_struct channel_cb;
|
||||
bool cc_didauth;
|
||||
bool cc_didchannel;
|
||||
bool cc_didpty;
|
||||
bool cc_didshell;
|
||||
int cc_cols;
|
||||
int cc_rows;
|
||||
int cc_py;
|
||||
int cc_px;
|
||||
char cc_term[16];
|
||||
SLIST_ENTRY(client_ctx) cc_client_list;
|
||||
void (*cc_begin_interactive_session)(struct interactive_session *);
|
||||
struct interactive_session cc_is;
|
||||
};
|
||||
|
||||
struct server_ctx {
|
||||
ssh_event sc_sshevent;
|
||||
ssh_bind sc_sshbind;
|
||||
struct ssh_server_callbacks_struct sc_server_cb;
|
||||
struct ssh_callbacks_struct sc_generic_cb;
|
||||
struct ssh_bind_callbacks_struct sc_bind_cb;
|
||||
int sc_auth_methods;
|
||||
struct ssh_user * (*sc_lookup_user)(struct server_ctx *, const char *);
|
||||
const char * sc_host_key;
|
||||
void (*sc_begin_interactive_session)(struct interactive_session *);
|
||||
SLIST_HEAD(, client_ctx) sc_client_head;
|
||||
};
|
||||
|
||||
int sshd_main(struct server_ctx *sc);
|
||||
96
examples/sshd/main/sshd_task.c
Normal file
96
examples/sshd/main/sshd_task.c
Normal file
@@ -0,0 +1,96 @@
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/server.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <sys/queue.h>
|
||||
#include "example.h"
|
||||
#include "sshd.h"
|
||||
|
||||
/* change this */
|
||||
static struct ssh_user hardcoded_example_users[] = {
|
||||
{
|
||||
.su_user = "neo",
|
||||
.su_password = "trinity",
|
||||
},
|
||||
{
|
||||
.su_user = "tnn",
|
||||
.su_keytype = SSH_KEYTYPE_ED25519,
|
||||
.su_base64_key = "AAAAC3NzaC1lZDI1NTE5AAAAILrLCwnBbitV0fhQyy7PClEDVLbtD3tzmuWX4fU6DuxI"
|
||||
},
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* obviously you'll want to replace this also */
|
||||
const char *hardcoded_example_host_key =
|
||||
"-----BEGIN OPENSSH PRIVATE KEY-----\n"
|
||||
"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\n"
|
||||
"-----END OPENSSH PRIVATE KEY-----\n";
|
||||
|
||||
static struct ssh_user *
|
||||
lookup_user(struct server_ctx *sc, const char *user)
|
||||
{
|
||||
struct ssh_user *su;
|
||||
for (su = hardcoded_example_users; su->su_user; su++) {
|
||||
if (strcmp(user, su->su_user) == 0)
|
||||
return su;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
sshd_task(void *arg)
|
||||
{
|
||||
struct server_ctx *sc;
|
||||
sc = calloc(1, sizeof(struct server_ctx));
|
||||
if (!sc)
|
||||
return;
|
||||
sc->sc_host_key = hardcoded_example_host_key;
|
||||
sc->sc_lookup_user = lookup_user;
|
||||
sc->sc_begin_interactive_session = minicli_begin_interactive_session;
|
||||
sc->sc_auth_methods = SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY;
|
||||
sshd_main(sc);
|
||||
}
|
||||
|
||||
void
|
||||
start_sshd(void)
|
||||
{
|
||||
xTaskCreate(sshd_task, "sshd", 32768, NULL, 10, NULL);
|
||||
}
|
||||
50
examples/sshd/main/wifi.c
Normal file
50
examples/sshd/main/wifi.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "example.h"
|
||||
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
const int DISCONNECTED_BIT = BIT1;
|
||||
|
||||
static void got_ip_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
|
||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||
}
|
||||
|
||||
static void disconnect_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
esp_wifi_connect();
|
||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||
xEventGroupSetBits(wifi_event_group, DISCONNECTED_BIT);
|
||||
}
|
||||
|
||||
void initialize_wifi(void)
|
||||
{
|
||||
tcpip_adapter_init();
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK( esp_event_loop_create_default() );
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, NULL) );
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &got_ip_handler, NULL) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) );
|
||||
ESP_ERROR_CHECK( esp_wifi_start() );
|
||||
}
|
||||
|
||||
void wifi_sta_join(const char* ssid, const char* pass)
|
||||
{
|
||||
wifi_config_t wifi_config = { 0 };
|
||||
|
||||
strlcpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
|
||||
strlcpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK( esp_wifi_connect() );
|
||||
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 5000/portTICK_RATE_MS);
|
||||
}
|
||||
1
examples/sshd/sdkconfig.defaults
Normal file
1
examples/sshd/sdkconfig.defaults
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_SPIFFS_USE_MTIME=n
|
||||
Reference in New Issue
Block a user