From fefb62e2c65aaf5864bcb90fbdd6bd1a091377e4 Mon Sep 17 00:00:00 2001 From: Tobias Nygren Date: Wed, 22 Jul 2020 22:57:10 +0200 Subject: [PATCH] add sample project --- examples/sshd/CMakeLists.txt | 4 + examples/sshd/main/CMakeLists.txt | 6 + examples/sshd/main/example.h | 13 + examples/sshd/main/main.c | 25 ++ examples/sshd/main/minicli.c | 142 ++++++++++ examples/sshd/main/sshd.c | 431 ++++++++++++++++++++++++++++++ examples/sshd/main/sshd.h | 39 +++ examples/sshd/main/sshd_task.c | 96 +++++++ examples/sshd/main/wifi.c | 50 ++++ examples/sshd/sdkconfig.defaults | 1 + 10 files changed, 807 insertions(+) create mode 100644 examples/sshd/CMakeLists.txt create mode 100644 examples/sshd/main/CMakeLists.txt create mode 100644 examples/sshd/main/example.h create mode 100644 examples/sshd/main/main.c create mode 100644 examples/sshd/main/minicli.c create mode 100644 examples/sshd/main/sshd.c create mode 100644 examples/sshd/main/sshd.h create mode 100644 examples/sshd/main/sshd_task.c create mode 100644 examples/sshd/main/wifi.c create mode 100644 examples/sshd/sdkconfig.defaults diff --git a/examples/sshd/CMakeLists.txt b/examples/sshd/CMakeLists.txt new file mode 100644 index 0000000..d63b932 --- /dev/null +++ b/examples/sshd/CMakeLists.txt @@ -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) diff --git a/examples/sshd/main/CMakeLists.txt b/examples/sshd/main/CMakeLists.txt new file mode 100644 index 0000000..19feff7 --- /dev/null +++ b/examples/sshd/main/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRCS "wifi.c" + "main.c" + "sshd.c" + "sshd_task.c" + "minicli.c" + INCLUDE_DIRS ".") diff --git a/examples/sshd/main/example.h b/examples/sshd/main/example.h new file mode 100644 index 0000000..1858db0 --- /dev/null +++ b/examples/sshd/main/example.h @@ -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 *); + diff --git a/examples/sshd/main/main.c b/examples/sshd/main/main.c new file mode 100644 index 0000000..cee0a5d --- /dev/null +++ b/examples/sshd/main/main.c @@ -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(); +} + diff --git a/examples/sshd/main/minicli.c b/examples/sshd/main/minicli.c new file mode 100644 index 0000000..59cfbd0 --- /dev/null +++ b/examples/sshd/main/minicli.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#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); +} diff --git a/examples/sshd/main/sshd.c b/examples/sshd/main/sshd.c new file mode 100644 index 0000000..598a5ff --- /dev/null +++ b/examples/sshd/main/sshd.c @@ -0,0 +1,431 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} + + diff --git a/examples/sshd/main/sshd.h b/examples/sshd/main/sshd.h new file mode 100644 index 0000000..79513d3 --- /dev/null +++ b/examples/sshd/main/sshd.h @@ -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); diff --git a/examples/sshd/main/sshd_task.c b/examples/sshd/main/sshd_task.c new file mode 100644 index 0000000..0bc95fa --- /dev/null +++ b/examples/sshd/main/sshd_task.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#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); +} diff --git a/examples/sshd/main/wifi.c b/examples/sshd/main/wifi.c new file mode 100644 index 0000000..ce1769d --- /dev/null +++ b/examples/sshd/main/wifi.c @@ -0,0 +1,50 @@ +#include +#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); +} diff --git a/examples/sshd/sdkconfig.defaults b/examples/sshd/sdkconfig.defaults new file mode 100644 index 0000000..60c404c --- /dev/null +++ b/examples/sshd/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_SPIFFS_USE_MTIME=n