| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- /*
- * ws protocol handler plugin for sshd demo
- *
- * Written in 2010-2019 by Andy Green <[email protected]>
- *
- * This file is made available under the Creative Commons CC0 1.0
- * Universal Public Domain Dedication.
- *
- * The person who associated a work with this deed has dedicated
- * the work to the public domain by waiving all of his or her rights
- * to the work worldwide under copyright law, including all related
- * and neighboring rights, to the extent allowed by law. You can copy,
- * modify, distribute and perform the work, even for commercial purposes,
- * all without asking permission.
- *
- * These test plugins are intended to be adapted for use in your code, which
- * may be proprietary. So unlike the library itself, they are licensed
- * Public Domain.
- */
- #if !defined (LWS_PLUGIN_STATIC)
- #define LWS_DLL
- #define LWS_INTERNAL
- #include <libwebsockets.h>
- #endif
- #include <lws-ssh.h>
- #include <string.h>
- #include <stdlib.h>
- #include <errno.h>
- #define TEST_SERVER_KEY_PATH "/etc/lws-test-sshd-server-key"
- struct per_vhost_data__lws_sshd_demo {
- const struct lws_protocols *ssh_base_protocol;
- int privileged_fd;
- };
- /*
- * This is a copy of the lws ssh test public key, you can find it in
- * /usr[/local]/share/libwebsockets-test-server/lws-ssh-test-keys.pub
- * and the matching private key there too in .../lws-ssh-test-keys
- *
- * If the vhost with this protocol is using localhost:2222, you can test with
- * the matching private key like this:
- *
- * ssh -p 2222 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys [email protected]
- *
- * These keys are distributed for testing! Don't use them on a real system
- * unless you want anyone with a copy of lws to access it.
- */
- static const char *authorized_key =
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCnWiP+c+kSD6Lk+C6NA9KruApa45sbt"
- "94/dxT0bCITlAA/+PBk6mR1lwWgXYozOMdrHrqx34piqDyXnc4HabqCaOm/FrYhkCPL8z"
- "a26PMYqteSosuwKv//5iT6ZWhNnsMwExBwtV6MIq0MxAeWqxRnYNWpNM8iN6sFzkdG/YF"
- "dyHrIBTgwzM77NLCMl6GEkJErRCFppC2SwYxGa3BRrgUwX3LkV8HpMIaYHFo1Qgj7Scqm"
- "HwS2R75SOqi2aOWDpKjznARg9JgzDWSQi4seBMV2oL0BTwJANSDf+p0sQLsaKGJhpVpBQ"
- "yS2wUeyuGyytupWzEluQrajMZq52iotcogv5BfeulfTTFbJP4kuHOsSP0lsQ2lpMDQANS"
- "HEvXxzHJLDLXM9gXJzwJ+ZiRt6R+bfmP1nfN3MiWtxcIbBanWwQK6xTCKBe4wPaKta5EU"
- "6wsLPeakOIVzoeaOu/HsbtPZlwX0Mu/oUFcfKyKAhlkU15MOAIEfUPo8Yh52bWMlIlpZa"
- "4xWbLMGw3GrsrPPdcsAauyqvY4/NjjWQbWhP1SuUfvv5709PIiOUjVKK2HUwmR1ouch6X"
- "MQGXfMR1h1Wjvc+bkNs17gCIrQnFilAZLC3Sm3Opiz/4LO99Hw448G0RM2vQn0mJE46w"
- "Eu/B10U6Jf4Efojhh1dk85BD1LTIb+N3Q== ssh-test-key@lws";
- enum states {
- SSH_TEST_GREET,
- SSH_TEST_PRESSED,
- SSH_TEST_DONE,
- };
- static const char * const strings[] =
- {
- /* SSH_TEST_GREET */
- "Thanks for logging to lws sshd server demo.\n\r"
- "\n\r"
- "This demo is very simple, it waits for you to press\n\r"
- "a key, and acknowledges it. Then press another key\n\r"
- "and it will exit. But actually that demos the basic\n\r"
- "sshd functions underneath. You can use the ops struct\n\r"
- "members to add a pty / shell or whatever you want.\n\r"
- "\n\r"
- "Press a key...\n\r",
- /* SSH_TEST_PRESSED */
- "Thanks for pressing a key. Press another to exit.\n\r",
- /* SSH_TEST_DONE */
- "Bye!\n\r"
- };
- struct sshd_instance_priv {
- struct lws *wsi;
- enum states state;
- const char *ptr;
- int pos;
- int len;
- };
- static void
- enter_state(struct sshd_instance_priv *priv, enum states state)
- {
- priv->state = state;
- priv->ptr = strings[state];
- priv->pos = 0;
- priv->len = (int)strlen(priv->ptr);
- lws_callback_on_writable(priv->wsi);
- }
- /* ops: channel lifecycle */
- static int
- ssh_ops_channel_create(struct lws *wsi, void **_priv)
- {
- struct sshd_instance_priv *priv;
- priv = malloc(sizeof(struct sshd_instance_priv));
- *_priv = priv;
- if (!priv)
- return 1;
- memset(priv, 0, sizeof(*priv));
- priv->wsi = wsi;
- return 0;
- }
- static int
- ssh_ops_channel_destroy(void *_priv)
- {
- struct sshd_instance_priv *priv = _priv;
- free(priv);
- return 0;
- }
- /* ops: IO */
- static int
- ssh_ops_tx_waiting(void *_priv)
- {
- struct sshd_instance_priv *priv = _priv;
- if (priv->state == SSH_TEST_DONE &&
- priv->pos == priv->len)
- return -1; /* exit */
- if (priv->pos != priv->len)
- return LWS_STDOUT;
- return 0;
- }
- static size_t
- ssh_ops_tx(void *_priv, int stdch, uint8_t *buf, size_t len)
- {
- struct sshd_instance_priv *priv = _priv;
- size_t chunk = len;
- if (stdch != LWS_STDOUT)
- return 0;
- if ((size_t)(priv->len - priv->pos) < chunk)
- chunk = priv->len - priv->pos;
- if (!chunk)
- return 0;
- memcpy(buf, priv->ptr + priv->pos, chunk);
- priv->pos += (int)chunk;
- if (priv->state == SSH_TEST_DONE && priv->pos == priv->len) {
- /*
- * we are sending the last thing we want to send
- * before exiting. Make it ask again at ssh_ops_tx_waiting()
- * and we will exit then, after this has been sent
- */
- lws_callback_on_writable(priv->wsi);
- }
- return chunk;
- }
- static int
- ssh_ops_rx(void *_priv, struct lws *wsi, const uint8_t *buf, uint32_t len)
- {
- struct sshd_instance_priv *priv = _priv;
- if (priv->state < SSH_TEST_DONE)
- enter_state(priv, priv->state + 1);
- else
- return -1;
- return 0;
- }
- /* ops: storage for the (autogenerated) persistent server key */
- static size_t
- ssh_ops_get_server_key(struct lws *wsi, uint8_t *buf, size_t len)
- {
- struct per_vhost_data__lws_sshd_demo *vhd =
- (struct per_vhost_data__lws_sshd_demo *)
- lws_protocol_vh_priv_get(lws_get_vhost(wsi),
- lws_get_protocol(wsi));
- int n;
- if (lseek(vhd->privileged_fd, 0, SEEK_SET) < 0)
- return 0;
- n = read(vhd->privileged_fd, buf, (int)len);
- if (n < 0) {
- lwsl_err("%s: read failed: %d\n", __func__, n);
- n = 0;
- }
- return n;
- }
- static size_t
- ssh_ops_set_server_key(struct lws *wsi, uint8_t *buf, size_t len)
- {
- struct per_vhost_data__lws_sshd_demo *vhd =
- (struct per_vhost_data__lws_sshd_demo *)
- lws_protocol_vh_priv_get(lws_get_vhost(wsi),
- lws_get_protocol(wsi));
- int n;
- n = write(vhd->privileged_fd, buf, (int)len);
- if (n < 0) {
- lwsl_err("%s: read failed: %d\n", __func__, errno);
- n = 0;
- }
- return n;
- }
- /* ops: auth */
- static int
- ssh_ops_is_pubkey_authorized(const char *username, const char *type,
- const uint8_t *peer, int peer_len)
- {
- char *aps, *p, *ps;
- int n = (int)strlen(type), alen = 2048, ret = 2, len;
- size_t s = 0;
- lwsl_info("%s: checking pubkey for %s\n", __func__, username);
- s = strlen(authorized_key) + 1;
- aps = malloc(s);
- if (!aps) {
- lwsl_notice("OOM 1\n");
- goto bail_p1;
- }
- memcpy(aps, authorized_key, s);
- /* we only understand RSA */
- if (strcmp(type, "ssh-rsa")) {
- lwsl_notice("type is not ssh-rsa\n");
- goto bail_p1;
- }
- p = aps;
- if (strncmp(p, type, n)) {
- lwsl_notice("lead-in string does not match %s\n", type);
- goto bail_p1;
- }
- p += n;
- if (*p != ' ') {
- lwsl_notice("missing space at end of lead-in\n");
- goto bail_p1;
- }
- p++;
- ps = malloc(alen);
- if (!ps) {
- lwsl_notice("OOM 2\n");
- free(aps);
- goto bail;
- }
- len = lws_b64_decode_string(p, ps, alen);
- free(aps);
- if (len < 0) {
- lwsl_notice("key too big\n");
- goto bail;
- }
- if (peer_len > len) {
- lwsl_notice("peer_len %d bigger than decoded len %d\n",
- peer_len, len);
- goto bail;
- }
- /*
- * once we are past that, it's the same <len32>name
- * <len32>E<len32>N that the peer sends us
- */
- if (memcmp(peer, ps, peer_len)) {
- lwsl_info("%s: factors mismatch, rejecting key\n", __func__);
- goto bail;
- }
- lwsl_info("pubkey authorized\n");
- ret = 0;
- bail:
- free(ps);
- return ret;
- bail_p1:
- if (aps)
- free(aps);
- return 1;
- }
- static int
- ssh_ops_shell(void *_priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle)
- {
- struct sshd_instance_priv *priv = _priv;
- /* for this demo, we don't open a real shell */
- enter_state(priv, SSH_TEST_GREET);
- return 0;
- }
- /* ops: banner */
- static size_t
- ssh_ops_banner(char *buf, size_t max_len, char *lang, size_t max_lang_len)
- {
- int n = lws_snprintf(buf, max_len, "\n"
- " |\\---/| lws-ssh Test Server\n"
- " | o_o | SSH Terminal Server\n"
- " \\_^_/ Copyright (C) 2017 Crash Barrier Ltd\n\n");
- lws_snprintf(lang, max_lang_len, "en/US");
- return n;
- }
- static void
- ssh_ops_disconnect_reason(uint32_t reason, const char *desc,
- const char *desc_lang)
- {
- lwsl_notice("DISCONNECT reason 0x%X, %s (lang %s)\n", reason, desc,
- desc_lang);
- }
- static const struct lws_ssh_ops ssh_ops = {
- .channel_create = ssh_ops_channel_create,
- .channel_destroy = ssh_ops_channel_destroy,
- .tx_waiting = ssh_ops_tx_waiting,
- .tx = ssh_ops_tx,
- .rx = ssh_ops_rx,
- .get_server_key = ssh_ops_get_server_key,
- .set_server_key = ssh_ops_set_server_key,
- .set_env = NULL,
- .pty_req = NULL,
- .child_process_io = NULL,
- .child_process_terminated = NULL,
- .exec = NULL,
- .shell = ssh_ops_shell,
- .is_pubkey_authorized = ssh_ops_is_pubkey_authorized,
- .banner = ssh_ops_banner,
- .disconnect_reason = ssh_ops_disconnect_reason,
- .server_string = "SSH-2.0-Libwebsockets",
- .api_version = 2,
- };
- static int
- callback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason,
- void *user, void *in, size_t len)
- {
- struct per_vhost_data__lws_sshd_demo *vhd =
- (struct per_vhost_data__lws_sshd_demo *)
- lws_protocol_vh_priv_get(lws_get_vhost(wsi),
- lws_get_protocol(wsi));
- switch (reason) {
- case LWS_CALLBACK_PROTOCOL_INIT:
- vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
- lws_get_protocol(wsi),
- sizeof(struct per_vhost_data__lws_sshd_demo));
- /*
- * During this we still have the privs / caps we were started
- * with. So open an fd on the server key, either just for read
- * or for creat / trunc if doesn't exist. This allows us to
- * deal with it down /etc/.. when just after this we will lose
- * the privileges needed to read / write /etc/...
- */
- vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY);
- if (vhd->privileged_fd == -1)
- vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH,
- O_CREAT | O_TRUNC | O_RDWR, 0600);
- if (vhd->privileged_fd == -1) {
- lwsl_err("%s: Can't open %s\n", __func__,
- TEST_SERVER_KEY_PATH);
- return -1;
- }
- break;
- case LWS_CALLBACK_PROTOCOL_DESTROY:
- close(vhd->privileged_fd);
- break;
- case LWS_CALLBACK_VHOST_CERT_AGING:
- break;
- case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
- break;
- default:
- if (!vhd->ssh_base_protocol) {
- vhd->ssh_base_protocol = lws_vhost_name_to_protocol(
- lws_get_vhost(wsi),
- "lws-ssh-base");
- if (vhd->ssh_base_protocol)
- user = lws_adjust_protocol_psds(wsi,
- vhd->ssh_base_protocol->per_session_data_size);
- }
- if (vhd->ssh_base_protocol)
- return vhd->ssh_base_protocol->callback(wsi, reason,
- user, in, len);
- else
- lwsl_notice("can't find lws-ssh-base\n");
- break;
- }
- return 0;
- }
- #define LWS_PLUGIN_PROTOCOL_LWS_SSHD_DEMO \
- { \
- "lws-sshd-demo", \
- callback_lws_sshd_demo, \
- 0, \
- 1024, /* rx buf size must be >= permessage-deflate rx size */ \
- 0, (void *)&ssh_ops, 0 \
- }
- #if !defined (LWS_PLUGIN_STATIC)
-
- static const struct lws_protocols protocols[] = {
- LWS_PLUGIN_PROTOCOL_LWS_SSHD_DEMO
- };
- LWS_VISIBLE const lws_plugin_protocol_t lws_sshd_demo = {
- .hdr = {
- "lws sshd demo",
- "lws_protocol_plugin",
- LWS_PLUGIN_API_MAGIC
- },
- .protocols = protocols,
- .count_protocols = LWS_ARRAY_SIZE(protocols),
- .extensions = NULL,
- .count_extensions = 0,
- };
- #endif
|