1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * ws protocol handler plugin for sshd demo
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0
7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication.
8d4afb5ceSopenharmony_ci *
9d4afb5ceSopenharmony_ci * The person who associated a work with this deed has dedicated
10d4afb5ceSopenharmony_ci * the work to the public domain by waiving all of his or her rights
11d4afb5ceSopenharmony_ci * to the work worldwide under copyright law, including all related
12d4afb5ceSopenharmony_ci * and neighboring rights, to the extent allowed by law. You can copy,
13d4afb5ceSopenharmony_ci * modify, distribute and perform the work, even for commercial purposes,
14d4afb5ceSopenharmony_ci * all without asking permission.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * These test plugins are intended to be adapted for use in your code, which
17d4afb5ceSopenharmony_ci * may be proprietary.  So unlike the library itself, they are licensed
18d4afb5ceSopenharmony_ci * Public Domain.
19d4afb5ceSopenharmony_ci */
20d4afb5ceSopenharmony_ci
21d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC)
22d4afb5ceSopenharmony_ci#if !defined(LWS_DLL)
23d4afb5ceSopenharmony_ci#define LWS_DLL
24d4afb5ceSopenharmony_ci#endif
25d4afb5ceSopenharmony_ci#if !defined(LWS_INTERNAL)
26d4afb5ceSopenharmony_ci#define LWS_INTERNAL
27d4afb5ceSopenharmony_ci#endif
28d4afb5ceSopenharmony_ci#include <libwebsockets.h>
29d4afb5ceSopenharmony_ci#endif
30d4afb5ceSopenharmony_ci
31d4afb5ceSopenharmony_ci#include <lws-ssh.h>
32d4afb5ceSopenharmony_ci
33d4afb5ceSopenharmony_ci#include <string.h>
34d4afb5ceSopenharmony_ci#include <stdlib.h>
35d4afb5ceSopenharmony_ci#include <errno.h>
36d4afb5ceSopenharmony_ci#include <fcntl.h>
37d4afb5ceSopenharmony_ci
38d4afb5ceSopenharmony_ci#define TEST_SERVER_KEY_PATH "/etc/lws-test-sshd-server-key"
39d4afb5ceSopenharmony_ci
40d4afb5ceSopenharmony_cistruct per_vhost_data__lws_sshd_demo {
41d4afb5ceSopenharmony_ci	const struct lws_protocols *ssh_base_protocol;
42d4afb5ceSopenharmony_ci	int privileged_fd;
43d4afb5ceSopenharmony_ci};
44d4afb5ceSopenharmony_ci
45d4afb5ceSopenharmony_ci/*
46d4afb5ceSopenharmony_ci *  This is a copy of the lws ssh test public key, you can find it in
47d4afb5ceSopenharmony_ci *  /usr[/local]/share/libwebsockets-test-server/lws-ssh-test-keys.pub
48d4afb5ceSopenharmony_ci *  and the matching private key there too in .../lws-ssh-test-keys
49d4afb5ceSopenharmony_ci *
50d4afb5ceSopenharmony_ci *  If the vhost with this protocol is using localhost:2222, you can test with
51d4afb5ceSopenharmony_ci *  the matching private key like this:
52d4afb5ceSopenharmony_ci *
53d4afb5ceSopenharmony_ci *  ssh -p 2222 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys anyuser@127.0.0.1
54d4afb5ceSopenharmony_ci *
55d4afb5ceSopenharmony_ci *  These keys are distributed for testing!  Don't use them on a real system
56d4afb5ceSopenharmony_ci *  unless you want anyone with a copy of lws to access it.
57d4afb5ceSopenharmony_ci */
58d4afb5ceSopenharmony_cistatic const char *authorized_key =
59d4afb5ceSopenharmony_ci	"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCnWiP+c+kSD6Lk+C6NA9KruApa45sbt"
60d4afb5ceSopenharmony_ci	"94/dxT0bCITlAA/+PBk6mR1lwWgXYozOMdrHrqx34piqDyXnc4HabqCaOm/FrYhkCPL8z"
61d4afb5ceSopenharmony_ci	"a26PMYqteSosuwKv//5iT6ZWhNnsMwExBwtV6MIq0MxAeWqxRnYNWpNM8iN6sFzkdG/YF"
62d4afb5ceSopenharmony_ci	"dyHrIBTgwzM77NLCMl6GEkJErRCFppC2SwYxGa3BRrgUwX3LkV8HpMIaYHFo1Qgj7Scqm"
63d4afb5ceSopenharmony_ci	"HwS2R75SOqi2aOWDpKjznARg9JgzDWSQi4seBMV2oL0BTwJANSDf+p0sQLsaKGJhpVpBQ"
64d4afb5ceSopenharmony_ci	"yS2wUeyuGyytupWzEluQrajMZq52iotcogv5BfeulfTTFbJP4kuHOsSP0lsQ2lpMDQANS"
65d4afb5ceSopenharmony_ci	"HEvXxzHJLDLXM9gXJzwJ+ZiRt6R+bfmP1nfN3MiWtxcIbBanWwQK6xTCKBe4wPaKta5EU"
66d4afb5ceSopenharmony_ci	"6wsLPeakOIVzoeaOu/HsbtPZlwX0Mu/oUFcfKyKAhlkU15MOAIEfUPo8Yh52bWMlIlpZa"
67d4afb5ceSopenharmony_ci	"4xWbLMGw3GrsrPPdcsAauyqvY4/NjjWQbWhP1SuUfvv5709PIiOUjVKK2HUwmR1ouch6X"
68d4afb5ceSopenharmony_ci	"MQGXfMR1h1Wjvc+bkNs17gCIrQnFilAZLC3Sm3Opiz/4LO99Hw448G0RM2vQn0mJE46w"
69d4afb5ceSopenharmony_ci	"Eu/B10U6Jf4Efojhh1dk85BD1LTIb+N3Q== ssh-test-key@lws";
70d4afb5ceSopenharmony_ci
71d4afb5ceSopenharmony_cienum states {
72d4afb5ceSopenharmony_ci	SSH_TEST_GREET,
73d4afb5ceSopenharmony_ci	SSH_TEST_PRESSED,
74d4afb5ceSopenharmony_ci	SSH_TEST_DONE,
75d4afb5ceSopenharmony_ci};
76d4afb5ceSopenharmony_ci
77d4afb5ceSopenharmony_cistatic const char * const strings[] =
78d4afb5ceSopenharmony_ci	{
79d4afb5ceSopenharmony_ci		/* SSH_TEST_GREET */
80d4afb5ceSopenharmony_ci		"Thanks for logging to lws sshd server demo.\n\r"
81d4afb5ceSopenharmony_ci		"\n\r"
82d4afb5ceSopenharmony_ci		"This demo is very simple, it waits for you to press\n\r"
83d4afb5ceSopenharmony_ci		"a key, and acknowledges it.  Then press another key\n\r"
84d4afb5ceSopenharmony_ci		"and it will exit.  But actually that demos the basic\n\r"
85d4afb5ceSopenharmony_ci		"sshd functions underneath.  You can use the ops struct\n\r"
86d4afb5ceSopenharmony_ci		"members to add a pty / shell or whatever you want.\n\r"
87d4afb5ceSopenharmony_ci		"\n\r"
88d4afb5ceSopenharmony_ci		"Press a key...\n\r",
89d4afb5ceSopenharmony_ci
90d4afb5ceSopenharmony_ci		/* SSH_TEST_PRESSED */
91d4afb5ceSopenharmony_ci		"Thanks for pressing a key.  Press another to exit.\n\r",
92d4afb5ceSopenharmony_ci
93d4afb5ceSopenharmony_ci		/* SSH_TEST_DONE */
94d4afb5ceSopenharmony_ci		"Bye!\n\r"
95d4afb5ceSopenharmony_ci	};
96d4afb5ceSopenharmony_ci
97d4afb5ceSopenharmony_cistruct sshd_instance_priv {
98d4afb5ceSopenharmony_ci	struct lws *wsi;
99d4afb5ceSopenharmony_ci	enum states state;
100d4afb5ceSopenharmony_ci	const char *ptr;
101d4afb5ceSopenharmony_ci	int pos;
102d4afb5ceSopenharmony_ci	int len;
103d4afb5ceSopenharmony_ci};
104d4afb5ceSopenharmony_ci
105d4afb5ceSopenharmony_cistatic void
106d4afb5ceSopenharmony_cienter_state(struct sshd_instance_priv *priv, enum states state)
107d4afb5ceSopenharmony_ci{
108d4afb5ceSopenharmony_ci	priv->state = state;
109d4afb5ceSopenharmony_ci	priv->ptr = strings[state];
110d4afb5ceSopenharmony_ci	priv->pos = 0;
111d4afb5ceSopenharmony_ci	priv->len = (int)strlen(priv->ptr);
112d4afb5ceSopenharmony_ci
113d4afb5ceSopenharmony_ci	lws_callback_on_writable(priv->wsi);
114d4afb5ceSopenharmony_ci}
115d4afb5ceSopenharmony_ci
116d4afb5ceSopenharmony_ci/* ops: channel lifecycle */
117d4afb5ceSopenharmony_ci
118d4afb5ceSopenharmony_cistatic int
119d4afb5ceSopenharmony_cissh_ops_channel_create(struct lws *wsi, void **_priv)
120d4afb5ceSopenharmony_ci{
121d4afb5ceSopenharmony_ci	struct sshd_instance_priv *priv;
122d4afb5ceSopenharmony_ci
123d4afb5ceSopenharmony_ci	priv = malloc(sizeof(struct sshd_instance_priv));
124d4afb5ceSopenharmony_ci	*_priv = priv;
125d4afb5ceSopenharmony_ci	if (!priv)
126d4afb5ceSopenharmony_ci		return 1;
127d4afb5ceSopenharmony_ci
128d4afb5ceSopenharmony_ci	memset(priv, 0, sizeof(*priv));
129d4afb5ceSopenharmony_ci	priv->wsi = wsi;
130d4afb5ceSopenharmony_ci
131d4afb5ceSopenharmony_ci	return 0;
132d4afb5ceSopenharmony_ci}
133d4afb5ceSopenharmony_ci
134d4afb5ceSopenharmony_cistatic int
135d4afb5ceSopenharmony_cissh_ops_channel_destroy(void *_priv)
136d4afb5ceSopenharmony_ci{
137d4afb5ceSopenharmony_ci	struct sshd_instance_priv *priv = _priv;
138d4afb5ceSopenharmony_ci
139d4afb5ceSopenharmony_ci	free(priv);
140d4afb5ceSopenharmony_ci
141d4afb5ceSopenharmony_ci	return 0;
142d4afb5ceSopenharmony_ci}
143d4afb5ceSopenharmony_ci
144d4afb5ceSopenharmony_ci/* ops: IO */
145d4afb5ceSopenharmony_ci
146d4afb5ceSopenharmony_cistatic int
147d4afb5ceSopenharmony_cissh_ops_tx_waiting(void *_priv)
148d4afb5ceSopenharmony_ci{
149d4afb5ceSopenharmony_ci	struct sshd_instance_priv *priv = _priv;
150d4afb5ceSopenharmony_ci
151d4afb5ceSopenharmony_ci	if (priv->state == SSH_TEST_DONE &&
152d4afb5ceSopenharmony_ci	    priv->pos == priv->len)
153d4afb5ceSopenharmony_ci		return -1; /* exit */
154d4afb5ceSopenharmony_ci
155d4afb5ceSopenharmony_ci	if (priv->pos != priv->len)
156d4afb5ceSopenharmony_ci		return LWS_STDOUT;
157d4afb5ceSopenharmony_ci
158d4afb5ceSopenharmony_ci	return 0;
159d4afb5ceSopenharmony_ci}
160d4afb5ceSopenharmony_ci
161d4afb5ceSopenharmony_cistatic size_t
162d4afb5ceSopenharmony_cissh_ops_tx(void *_priv, int stdch, uint8_t *buf, size_t len)
163d4afb5ceSopenharmony_ci{
164d4afb5ceSopenharmony_ci	struct sshd_instance_priv *priv = _priv;
165d4afb5ceSopenharmony_ci	size_t chunk = len;
166d4afb5ceSopenharmony_ci
167d4afb5ceSopenharmony_ci	if (stdch != LWS_STDOUT)
168d4afb5ceSopenharmony_ci		return 0;
169d4afb5ceSopenharmony_ci
170d4afb5ceSopenharmony_ci	if ((size_t)(priv->len - priv->pos) < chunk)
171d4afb5ceSopenharmony_ci		chunk = (size_t)(priv->len - priv->pos);
172d4afb5ceSopenharmony_ci
173d4afb5ceSopenharmony_ci	if (!chunk)
174d4afb5ceSopenharmony_ci		return 0;
175d4afb5ceSopenharmony_ci
176d4afb5ceSopenharmony_ci	memcpy(buf, priv->ptr + priv->pos, chunk);
177d4afb5ceSopenharmony_ci	priv->pos += (int)chunk;
178d4afb5ceSopenharmony_ci
179d4afb5ceSopenharmony_ci	if (priv->state == SSH_TEST_DONE && priv->pos == priv->len) {
180d4afb5ceSopenharmony_ci		/*
181d4afb5ceSopenharmony_ci		 * we are sending the last thing we want to send
182d4afb5ceSopenharmony_ci		 * before exiting.  Make it ask again at ssh_ops_tx_waiting()
183d4afb5ceSopenharmony_ci		 * and we will exit then, after this has been sent
184d4afb5ceSopenharmony_ci		 */
185d4afb5ceSopenharmony_ci		lws_callback_on_writable(priv->wsi);
186d4afb5ceSopenharmony_ci	}
187d4afb5ceSopenharmony_ci
188d4afb5ceSopenharmony_ci	return chunk;
189d4afb5ceSopenharmony_ci}
190d4afb5ceSopenharmony_ci
191d4afb5ceSopenharmony_ci
192d4afb5ceSopenharmony_cistatic int
193d4afb5ceSopenharmony_cissh_ops_rx(void *_priv, struct lws *wsi, const uint8_t *buf, uint32_t len)
194d4afb5ceSopenharmony_ci{
195d4afb5ceSopenharmony_ci	struct sshd_instance_priv *priv = _priv;
196d4afb5ceSopenharmony_ci
197d4afb5ceSopenharmony_ci	if (priv->state < SSH_TEST_DONE)
198d4afb5ceSopenharmony_ci		enter_state(priv, priv->state + 1);
199d4afb5ceSopenharmony_ci	else
200d4afb5ceSopenharmony_ci		return -1;
201d4afb5ceSopenharmony_ci
202d4afb5ceSopenharmony_ci	return 0;
203d4afb5ceSopenharmony_ci}
204d4afb5ceSopenharmony_ci
205d4afb5ceSopenharmony_ci/* ops: storage for the (autogenerated) persistent server key */
206d4afb5ceSopenharmony_ci
207d4afb5ceSopenharmony_cistatic size_t
208d4afb5ceSopenharmony_cissh_ops_get_server_key(struct lws *wsi, uint8_t *buf, size_t len)
209d4afb5ceSopenharmony_ci{
210d4afb5ceSopenharmony_ci	struct per_vhost_data__lws_sshd_demo *vhd =
211d4afb5ceSopenharmony_ci			(struct per_vhost_data__lws_sshd_demo *)
212d4afb5ceSopenharmony_ci			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
213d4afb5ceSopenharmony_ci						 lws_get_protocol(wsi));
214d4afb5ceSopenharmony_ci	int n;
215d4afb5ceSopenharmony_ci
216d4afb5ceSopenharmony_ci	if (lseek(vhd->privileged_fd, 0, SEEK_SET) < 0)
217d4afb5ceSopenharmony_ci		return 0;
218d4afb5ceSopenharmony_ci	n = (int)read(vhd->privileged_fd, buf, (unsigned int)len);
219d4afb5ceSopenharmony_ci	if (n < 0) {
220d4afb5ceSopenharmony_ci		lwsl_err("%s: read failed: %d\n", __func__, n);
221d4afb5ceSopenharmony_ci		n = 0;
222d4afb5ceSopenharmony_ci	}
223d4afb5ceSopenharmony_ci
224d4afb5ceSopenharmony_ci	return (size_t)n;
225d4afb5ceSopenharmony_ci}
226d4afb5ceSopenharmony_ci
227d4afb5ceSopenharmony_cistatic size_t
228d4afb5ceSopenharmony_cissh_ops_set_server_key(struct lws *wsi, uint8_t *buf, size_t len)
229d4afb5ceSopenharmony_ci{
230d4afb5ceSopenharmony_ci	struct per_vhost_data__lws_sshd_demo *vhd =
231d4afb5ceSopenharmony_ci			(struct per_vhost_data__lws_sshd_demo *)
232d4afb5ceSopenharmony_ci			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
233d4afb5ceSopenharmony_ci						 lws_get_protocol(wsi));
234d4afb5ceSopenharmony_ci	int n;
235d4afb5ceSopenharmony_ci
236d4afb5ceSopenharmony_ci	n = (int)write(vhd->privileged_fd, buf, (unsigned int)len);
237d4afb5ceSopenharmony_ci	if (n < 0) {
238d4afb5ceSopenharmony_ci		lwsl_err("%s: read failed: %d\n", __func__, errno);
239d4afb5ceSopenharmony_ci		n = 0;
240d4afb5ceSopenharmony_ci	}
241d4afb5ceSopenharmony_ci
242d4afb5ceSopenharmony_ci	return (size_t)n;
243d4afb5ceSopenharmony_ci}
244d4afb5ceSopenharmony_ci
245d4afb5ceSopenharmony_ci/* ops: auth */
246d4afb5ceSopenharmony_ci
247d4afb5ceSopenharmony_cistatic int
248d4afb5ceSopenharmony_cissh_ops_is_pubkey_authorized(const char *username, const char *type,
249d4afb5ceSopenharmony_ci				 const uint8_t *peer, int peer_len)
250d4afb5ceSopenharmony_ci{
251d4afb5ceSopenharmony_ci	char *aps, *p, *ps;
252d4afb5ceSopenharmony_ci	int n = (int)strlen(type), alen = 2048, ret = 2, len;
253d4afb5ceSopenharmony_ci	size_t s = 0;
254d4afb5ceSopenharmony_ci
255d4afb5ceSopenharmony_ci	lwsl_info("%s: checking pubkey for %s\n", __func__, username);
256d4afb5ceSopenharmony_ci
257d4afb5ceSopenharmony_ci	s = strlen(authorized_key) + 1;
258d4afb5ceSopenharmony_ci
259d4afb5ceSopenharmony_ci	aps = malloc(s);
260d4afb5ceSopenharmony_ci	if (!aps) {
261d4afb5ceSopenharmony_ci		lwsl_notice("OOM 1\n");
262d4afb5ceSopenharmony_ci		goto bail_p1;
263d4afb5ceSopenharmony_ci	}
264d4afb5ceSopenharmony_ci	memcpy(aps, authorized_key, s);
265d4afb5ceSopenharmony_ci
266d4afb5ceSopenharmony_ci	/* we only understand RSA */
267d4afb5ceSopenharmony_ci	if (strcmp(type, "ssh-rsa")) {
268d4afb5ceSopenharmony_ci		lwsl_notice("type is not ssh-rsa\n");
269d4afb5ceSopenharmony_ci		goto bail_p1;
270d4afb5ceSopenharmony_ci	}
271d4afb5ceSopenharmony_ci	p = aps;
272d4afb5ceSopenharmony_ci
273d4afb5ceSopenharmony_ci	if (strncmp(p, type, (unsigned int)n)) {
274d4afb5ceSopenharmony_ci		lwsl_notice("lead-in string  does not match %s\n", type);
275d4afb5ceSopenharmony_ci		goto bail_p1;
276d4afb5ceSopenharmony_ci	}
277d4afb5ceSopenharmony_ci
278d4afb5ceSopenharmony_ci	p += n;
279d4afb5ceSopenharmony_ci	if (*p != ' ') {
280d4afb5ceSopenharmony_ci		lwsl_notice("missing space at end of lead-in\n");
281d4afb5ceSopenharmony_ci		goto bail_p1;
282d4afb5ceSopenharmony_ci	}
283d4afb5ceSopenharmony_ci
284d4afb5ceSopenharmony_ci	p++;
285d4afb5ceSopenharmony_ci	ps = malloc((unsigned int)alen);
286d4afb5ceSopenharmony_ci	if (!ps) {
287d4afb5ceSopenharmony_ci		lwsl_notice("OOM 2\n");
288d4afb5ceSopenharmony_ci		free(aps);
289d4afb5ceSopenharmony_ci		goto bail;
290d4afb5ceSopenharmony_ci	}
291d4afb5ceSopenharmony_ci	len = lws_b64_decode_string(p, ps, alen);
292d4afb5ceSopenharmony_ci	free(aps);
293d4afb5ceSopenharmony_ci	if (len < 0) {
294d4afb5ceSopenharmony_ci		lwsl_notice("key too big\n");
295d4afb5ceSopenharmony_ci		goto bail;
296d4afb5ceSopenharmony_ci	}
297d4afb5ceSopenharmony_ci
298d4afb5ceSopenharmony_ci	if (peer_len > len) {
299d4afb5ceSopenharmony_ci		lwsl_notice("peer_len %d bigger than decoded len %d\n",
300d4afb5ceSopenharmony_ci				peer_len, len);
301d4afb5ceSopenharmony_ci		goto bail;
302d4afb5ceSopenharmony_ci	}
303d4afb5ceSopenharmony_ci
304d4afb5ceSopenharmony_ci	/*
305d4afb5ceSopenharmony_ci	 * once we are past that, it's the same <len32>name
306d4afb5ceSopenharmony_ci	 * <len32>E<len32>N that the peer sends us
307d4afb5ceSopenharmony_ci	 */
308d4afb5ceSopenharmony_ci	if (memcmp(peer, ps, (unsigned int)peer_len)) {
309d4afb5ceSopenharmony_ci		lwsl_info("%s: factors mismatch, rejecting key\n", __func__);
310d4afb5ceSopenharmony_ci		goto bail;
311d4afb5ceSopenharmony_ci	}
312d4afb5ceSopenharmony_ci
313d4afb5ceSopenharmony_ci	lwsl_info("pubkey authorized\n");
314d4afb5ceSopenharmony_ci
315d4afb5ceSopenharmony_ci	ret = 0;
316d4afb5ceSopenharmony_cibail:
317d4afb5ceSopenharmony_ci	free(ps);
318d4afb5ceSopenharmony_ci
319d4afb5ceSopenharmony_ci	return ret;
320d4afb5ceSopenharmony_ci
321d4afb5ceSopenharmony_cibail_p1:
322d4afb5ceSopenharmony_ci	if (aps)
323d4afb5ceSopenharmony_ci		free(aps);
324d4afb5ceSopenharmony_ci
325d4afb5ceSopenharmony_ci	return 1;
326d4afb5ceSopenharmony_ci}
327d4afb5ceSopenharmony_ci
328d4afb5ceSopenharmony_cistatic int
329d4afb5ceSopenharmony_cissh_ops_shell(void *_priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle)
330d4afb5ceSopenharmony_ci{
331d4afb5ceSopenharmony_ci	struct sshd_instance_priv *priv = _priv;
332d4afb5ceSopenharmony_ci
333d4afb5ceSopenharmony_ci	/* for this demo, we don't open a real shell */
334d4afb5ceSopenharmony_ci
335d4afb5ceSopenharmony_ci	enter_state(priv, SSH_TEST_GREET);
336d4afb5ceSopenharmony_ci
337d4afb5ceSopenharmony_ci	return 0;
338d4afb5ceSopenharmony_ci}
339d4afb5ceSopenharmony_ci
340d4afb5ceSopenharmony_ci/* ops: banner */
341d4afb5ceSopenharmony_ci
342d4afb5ceSopenharmony_cistatic size_t
343d4afb5ceSopenharmony_cissh_ops_banner(char *buf, size_t max_len, char *lang, size_t max_lang_len)
344d4afb5ceSopenharmony_ci{
345d4afb5ceSopenharmony_ci	int n = lws_snprintf(buf, max_len, "\n"
346d4afb5ceSopenharmony_ci		      " |\\---/|  lws-ssh Test Server\n"
347d4afb5ceSopenharmony_ci		      " | o_o |  SSH Terminal Server\n"
348d4afb5ceSopenharmony_ci		      "  \\_^_/   Copyright (C) 2017 Crash Barrier Ltd\n\n");
349d4afb5ceSopenharmony_ci
350d4afb5ceSopenharmony_ci	lws_snprintf(lang, max_lang_len, "en/US");
351d4afb5ceSopenharmony_ci
352d4afb5ceSopenharmony_ci	return (size_t)n;
353d4afb5ceSopenharmony_ci}
354d4afb5ceSopenharmony_ci
355d4afb5ceSopenharmony_cistatic void
356d4afb5ceSopenharmony_cissh_ops_disconnect_reason(uint32_t reason, const char *desc,
357d4afb5ceSopenharmony_ci			  const char *desc_lang)
358d4afb5ceSopenharmony_ci{
359d4afb5ceSopenharmony_ci	lwsl_notice("DISCONNECT reason 0x%X, %s (lang %s)\n", reason, desc,
360d4afb5ceSopenharmony_ci		    desc_lang);
361d4afb5ceSopenharmony_ci}
362d4afb5ceSopenharmony_ci
363d4afb5ceSopenharmony_ci
364d4afb5ceSopenharmony_cistatic const struct lws_ssh_ops ssh_ops = {
365d4afb5ceSopenharmony_ci	.channel_create			= ssh_ops_channel_create,
366d4afb5ceSopenharmony_ci	.channel_destroy		= ssh_ops_channel_destroy,
367d4afb5ceSopenharmony_ci	.tx_waiting			= ssh_ops_tx_waiting,
368d4afb5ceSopenharmony_ci	.tx				= ssh_ops_tx,
369d4afb5ceSopenharmony_ci	.rx				= ssh_ops_rx,
370d4afb5ceSopenharmony_ci	.get_server_key			= ssh_ops_get_server_key,
371d4afb5ceSopenharmony_ci	.set_server_key			= ssh_ops_set_server_key,
372d4afb5ceSopenharmony_ci	.set_env			= NULL,
373d4afb5ceSopenharmony_ci	.pty_req			= NULL,
374d4afb5ceSopenharmony_ci	.child_process_io		= NULL,
375d4afb5ceSopenharmony_ci	.child_process_terminated	= NULL,
376d4afb5ceSopenharmony_ci	.exec				= NULL,
377d4afb5ceSopenharmony_ci	.shell				= ssh_ops_shell,
378d4afb5ceSopenharmony_ci	.is_pubkey_authorized		= ssh_ops_is_pubkey_authorized,
379d4afb5ceSopenharmony_ci	.banner				= ssh_ops_banner,
380d4afb5ceSopenharmony_ci	.disconnect_reason		= ssh_ops_disconnect_reason,
381d4afb5ceSopenharmony_ci	.server_string			= "SSH-2.0-Libwebsockets",
382d4afb5ceSopenharmony_ci	.api_version			= 2,
383d4afb5ceSopenharmony_ci};
384d4afb5ceSopenharmony_ci
385d4afb5ceSopenharmony_cistatic int
386d4afb5ceSopenharmony_cicallback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason,
387d4afb5ceSopenharmony_ci		       void *user, void *in, size_t len)
388d4afb5ceSopenharmony_ci{
389d4afb5ceSopenharmony_ci	struct per_vhost_data__lws_sshd_demo *vhd =
390d4afb5ceSopenharmony_ci			(struct per_vhost_data__lws_sshd_demo *)
391d4afb5ceSopenharmony_ci			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
392d4afb5ceSopenharmony_ci						 lws_get_protocol(wsi));
393d4afb5ceSopenharmony_ci
394d4afb5ceSopenharmony_ci	switch (reason) {
395d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_INIT:
396d4afb5ceSopenharmony_ci		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
397d4afb5ceSopenharmony_ci						  lws_get_protocol(wsi),
398d4afb5ceSopenharmony_ci				sizeof(struct per_vhost_data__lws_sshd_demo));
399d4afb5ceSopenharmony_ci		if (!vhd)
400d4afb5ceSopenharmony_ci			return 0;
401d4afb5ceSopenharmony_ci		/*
402d4afb5ceSopenharmony_ci		 * During this we still have the privs / caps we were started
403d4afb5ceSopenharmony_ci		 * with.  So open an fd on the server key, either just for read
404d4afb5ceSopenharmony_ci		 * or for creat / trunc if doesn't exist.  This allows us to
405d4afb5ceSopenharmony_ci		 * deal with it down /etc/.. when just after this we will lose
406d4afb5ceSopenharmony_ci		 * the privileges needed to read / write /etc/...
407d4afb5ceSopenharmony_ci		 */
408d4afb5ceSopenharmony_ci		vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY);
409d4afb5ceSopenharmony_ci		if (vhd->privileged_fd == -1)
410d4afb5ceSopenharmony_ci			vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH,
411d4afb5ceSopenharmony_ci					O_CREAT | O_TRUNC | O_RDWR, 0600);
412d4afb5ceSopenharmony_ci		if (vhd->privileged_fd == -1) {
413d4afb5ceSopenharmony_ci			lwsl_warn("%s: Can't open %s\n", __func__,
414d4afb5ceSopenharmony_ci				 TEST_SERVER_KEY_PATH);
415d4afb5ceSopenharmony_ci			return 0;
416d4afb5ceSopenharmony_ci		}
417d4afb5ceSopenharmony_ci		break;
418d4afb5ceSopenharmony_ci
419d4afb5ceSopenharmony_ci	case LWS_CALLBACK_PROTOCOL_DESTROY:
420d4afb5ceSopenharmony_ci		if (vhd)
421d4afb5ceSopenharmony_ci			close(vhd->privileged_fd);
422d4afb5ceSopenharmony_ci		break;
423d4afb5ceSopenharmony_ci
424d4afb5ceSopenharmony_ci	case LWS_CALLBACK_VHOST_CERT_AGING:
425d4afb5ceSopenharmony_ci		break;
426d4afb5ceSopenharmony_ci
427d4afb5ceSopenharmony_ci	case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
428d4afb5ceSopenharmony_ci		break;
429d4afb5ceSopenharmony_ci
430d4afb5ceSopenharmony_ci	default:
431d4afb5ceSopenharmony_ci		if (!vhd->ssh_base_protocol) {
432d4afb5ceSopenharmony_ci			vhd->ssh_base_protocol = lws_vhost_name_to_protocol(
433d4afb5ceSopenharmony_ci							lws_get_vhost(wsi),
434d4afb5ceSopenharmony_ci							"lws-ssh-base");
435d4afb5ceSopenharmony_ci			if (vhd->ssh_base_protocol)
436d4afb5ceSopenharmony_ci				user = lws_adjust_protocol_psds(wsi,
437d4afb5ceSopenharmony_ci				vhd->ssh_base_protocol->per_session_data_size);
438d4afb5ceSopenharmony_ci		}
439d4afb5ceSopenharmony_ci
440d4afb5ceSopenharmony_ci		if (vhd->ssh_base_protocol)
441d4afb5ceSopenharmony_ci			return vhd->ssh_base_protocol->callback(wsi, reason,
442d4afb5ceSopenharmony_ci								user, in, len);
443d4afb5ceSopenharmony_ci		else
444d4afb5ceSopenharmony_ci			lwsl_notice("can't find lws-ssh-base\n");
445d4afb5ceSopenharmony_ci		break;
446d4afb5ceSopenharmony_ci	}
447d4afb5ceSopenharmony_ci
448d4afb5ceSopenharmony_ci	return 0;
449d4afb5ceSopenharmony_ci}
450d4afb5ceSopenharmony_ci
451d4afb5ceSopenharmony_ci#define LWS_PLUGIN_PROTOCOL_LWS_SSHD_DEMO \
452d4afb5ceSopenharmony_ci	{ \
453d4afb5ceSopenharmony_ci		"lws-sshd-demo", \
454d4afb5ceSopenharmony_ci		callback_lws_sshd_demo, \
455d4afb5ceSopenharmony_ci		0, \
456d4afb5ceSopenharmony_ci		1024, /* rx buf size must be >= permessage-deflate rx size */ \
457d4afb5ceSopenharmony_ci		0, (void *)&ssh_ops, 0 \
458d4afb5ceSopenharmony_ci	}
459d4afb5ceSopenharmony_ci
460d4afb5ceSopenharmony_ci#if !defined (LWS_PLUGIN_STATIC)
461d4afb5ceSopenharmony_ci
462d4afb5ceSopenharmony_ciLWS_VISIBLE const struct lws_protocols lws_sshd_demo_protocols[] = {
463d4afb5ceSopenharmony_ci		LWS_PLUGIN_PROTOCOL_LWS_SSHD_DEMO
464d4afb5ceSopenharmony_ci};
465d4afb5ceSopenharmony_ci
466d4afb5ceSopenharmony_ciLWS_VISIBLE const lws_plugin_protocol_t lws_sshd_demo = {
467d4afb5ceSopenharmony_ci	.hdr = {
468d4afb5ceSopenharmony_ci		"lws sshd demo",
469d4afb5ceSopenharmony_ci		"lws_protocol_plugin",
470d4afb5ceSopenharmony_ci		LWS_BUILD_HASH,
471d4afb5ceSopenharmony_ci		LWS_PLUGIN_API_MAGIC
472d4afb5ceSopenharmony_ci	},
473d4afb5ceSopenharmony_ci
474d4afb5ceSopenharmony_ci	.protocols = lws_sshd_demo_protocols,
475d4afb5ceSopenharmony_ci	.count_protocols = LWS_ARRAY_SIZE(lws_sshd_demo_protocols),
476d4afb5ceSopenharmony_ci	.extensions = NULL,
477d4afb5ceSopenharmony_ci	.count_extensions = 0,
478d4afb5ceSopenharmony_ci};
479d4afb5ceSopenharmony_ci
480d4afb5ceSopenharmony_ci#endif
481