add example

This commit is contained in:
2025-07-07 12:00:40 +09:00
commit 40c55e20c9
13 changed files with 809 additions and 0 deletions

6
CMakeLists.txt Normal file
View File

@@ -0,0 +1,6 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(libssh_esp_test)

7
data/key Normal file
View File

@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAO887NGeWZKzgMdaAUKvFQ9AB/6Oi2L4Y8XMhyeY67igAAAIjP2a+jz9mv
owAAAAtzc2gtZWQyNTUxOQAAACAO887NGeWZKzgMdaAUKvFQ9AB/6Oi2L4Y8XMhyeY67ig
AAAEDPZnZR+VuirVhLMki657tEGFHYmivNignYjNlZHE3iXQ7zzs0Z5ZkrOAx1oBQq8VD0
AH/o6LYvhjxcyHJ5jruKAAAAAAECAwQF
-----END OPENSSH PRIVATE KEY-----

1
data/key.pub Normal file
View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA7zzs0Z5ZkrOAx1oBQq8VD0AH/o6LYvhjxcyHJ5jruK

8
main/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
idf_component_register(SRCS
"main.c"
"sshd.c"
"sshd_task.c"
"minicli.c"
INCLUDE_DIRS ".")
littlefs_create_partition_image(storage ../data FLASH_IN_PROJECT)

12
main/example.h Normal file
View File

@@ -0,0 +1,12 @@
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 *);

6
main/idf_component.yml Normal file
View File

@@ -0,0 +1,6 @@
dependencies:
joltwallet/littlefs: ~=1.20.1
libssh:
git: https://git.sys114.com/shinys000114/libssh_esp.git
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

29
main/main.c Normal file
View File

@@ -0,0 +1,29 @@
#include <freertos/FreeRTOS.h>
#include <esp_event.h>
#include <esp_netif.h>
#include <protocol_examples_common.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)
{
ESP_ERROR_CHECK(esp_netif_init());
initialize_nvs();
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
start_sshd();
}

142
main/minicli.c Normal file
View 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);
}

10
main/sdkconfig.defaults Normal file
View File

@@ -0,0 +1,10 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_LWIP_NETIF_API=y
CONFIG_UART_ISR_IN_IRAM=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y

430
main/sshd.c Normal file
View File

@@ -0,0 +1,430 @@
#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;
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
main/sshd.h Normal file
View 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);

113
main/sshd_task.c Normal file
View File

@@ -0,0 +1,113 @@
#include <esp_littlefs.h>
#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"
#define BASE_PATH "/littlefs"
#define CERT_KEY_PATH BASE_PATH "/key"
#define CERT_PUB_PATH BASE_PATH "/key.pub"
#define BUF_SIZE 256
static char key[BUF_SIZE];
static struct ssh_user hardcoded_example_users[] = {
{
.su_user = "odroid",
.su_password = "odroid",
},
{
}
};
static esp_err_t init_storage()
{
esp_err_t ret;
esp_vfs_littlefs_conf_t conf = {
.base_path = "/littlefs",
.partition_label = "storage",
.format_if_mount_failed = true,
.dont_mount = false,
};
ret = esp_vfs_littlefs_register(&conf);
if (ret != ESP_OK)
{
return ret;
}
return ESP_OK;
}
static esp_err_t load_key_pair(char *path, char* buf, size_t buf_size)
{
FILE *fp;
size_t size;
fp = fopen(path, "r");
esp_err_t ret;
if (fp == NULL)
{
return ESP_FAIL;
}
memset(buf, 0, buf_size);
fseek(fp, 0, SEEK_END);
size = ftell(fp);
rewind(fp);
if (fread(buf, 1, buf_size, fp) == size)
{
ret = ESP_OK;
}
else
{
ret = ESP_FAIL;
}
fclose(fp);
return ret;
}
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 = key;
sc->sc_lookup_user = lookup_user;
sc->sc_begin_interactive_session = minicli_begin_interactive_session;
sc->sc_auth_methods = SSH_AUTH_METHOD_PASSWORD;
sshd_main(sc);
vTaskDelete(NULL);
}
void
start_sshd(void)
{
init_storage();
load_key_pair(CERT_KEY_PATH, key, BUF_SIZE);
puts(key);
xTaskCreate(sshd_task, "sshd", 32768, NULL, 10, NULL);
}

6
partitions.csv Normal file
View File

@@ -0,0 +1,6 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,data,nvs,0x9000,24K,
phy_init,data,phy,0xf000,4K,
factory,app,factory,0x10000,2M,
storage,data,littlefs,0x210000,8K,
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs,data,nvs,0x9000,24K,
4 phy_init,data,phy,0xf000,4K,
5 factory,app,factory,0x10000,2M,
6 storage,data,littlefs,0x210000,8K,