xref: /aosp_15_r20/external/libwebsockets/test-apps/test-sshd.c (revision 1c60b9aca93fdbc9b5f19b2d2194c91294b22281)
1 /*
2  * Example embedded sshd server using libwebsockets sshd plugin
3  *
4  * Written in 2010-2019 by Andy Green <[email protected]>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * The person who associated a work with this deed has dedicated
10  * the work to the public domain by waiving all of his or her rights
11  * to the work worldwide under copyright law, including all related
12  * and neighboring rights, to the extent allowed by law. You can copy,
13  * modify, distribute and perform the work, even for commercial purposes,
14  * all without asking permission.
15  *
16  * The test apps are intended to be adapted for use in your code, which
17  * may be proprietary.	So unlike the library itself, they are licensed
18  * Public Domain.
19  *
20  *
21  * This test app listens on port 2200 for authorized ssh connections.  Run it
22  * using
23  *
24  * $ sudo libwebsockets-test-sshd
25  *
26  * Connect to it using the test private key with:
27  *
28  * $ ssh -p 2200 -i /usr/local/share/libwebsockets-test-server/lws-ssh-test-keys [email protected]
29  */
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <errno.h>
37 /* import the whole of lws-plugin-sshd-base statically */
38 #include <lws-plugin-sshd-static-build-includes.h>
39 
40 /*
41  * We store the test server's own key here (will be created with a new
42  * random key if it doesn't exist
43  *
44  * The /etc path is the only reason we have to run as root.
45  */
46 #define TEST_SERVER_KEY_PATH "/etc/lws-test-sshd-server-key"
47 struct per_vhost_data__lws_sshd_demo {
48 	const struct lws_protocols *ssh_base_protocol;
49 	int privileged_fd;
50 };
51 
52 
53 /*
54  *  This is a copy of the lws ssh test public key, you can find it in
55  *  /usr[/local]/share/libwebsockets-test-server/lws-ssh-test-keys.pub
56  *  and the matching private key there too in .../lws-ssh-test-keys
57  *
58  *  These keys are distributed for testing!  Don't use them on a real system
59  *  unless you want anyone with a copy of lws to access it.
60  */
61 static const char *authorized_key =
62 	"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCnWiP+c+kSD6Lk+C6NA9KruApa45sbt"
63 	"94/dxT0bCITlAA/+PBk6mR1lwWgXYozOMdrHrqx34piqDyXnc4HabqCaOm/FrYhkCPL8z"
64 	"a26PMYqteSosuwKv//5iT6ZWhNnsMwExBwtV6MIq0MxAeWqxRnYNWpNM8iN6sFzkdG/YF"
65 	"dyHrIBTgwzM77NLCMl6GEkJErRCFppC2SwYxGa3BRrgUwX3LkV8HpMIaYHFo1Qgj7Scqm"
66 	"HwS2R75SOqi2aOWDpKjznARg9JgzDWSQi4seBMV2oL0BTwJANSDf+p0sQLsaKGJhpVpBQ"
67 	"yS2wUeyuGyytupWzEluQrajMZq52iotcogv5BfeulfTTFbJP4kuHOsSP0lsQ2lpMDQANS"
68 	"HEvXxzHJLDLXM9gXJzwJ+ZiRt6R+bfmP1nfN3MiWtxcIbBanWwQK6xTCKBe4wPaKta5EU"
69 	"6wsLPeakOIVzoeaOu/HsbtPZlwX0Mu/oUFcfKyKAhlkU15MOAIEfUPo8Yh52bWMlIlpZa"
70 	"4xWbLMGw3GrsrPPdcsAauyqvY4/NjjWQbWhP1SuUfvv5709PIiOUjVKK2HUwmR1ouch6X"
71 	"MQGXfMR1h1Wjvc+bkNs17gCIrQnFilAZLC3Sm3Opiz/4LO99Hw448G0RM2vQn0mJE46w"
72 	"Eu/B10U6Jf4Efojhh1dk85BD1LTIb+N3Q== ssh-test-key@lws";
73 
74 static struct lws_context *context = NULL;
75 static volatile char force_exit = 0;
76 
77 /*
78  * These are our "ops" that form our customization of, and interface to, the
79  * generic sshd plugin.
80  *
81  * The priv struct contains our data we want to associate to each channel
82  * individually.
83  */
84 
85 struct sshd_instance_priv {
86 	struct lws_protocol_vhost_options *env;
87 	struct lws_ring	*ring_stdout;
88 	struct lws_ring	*ring_stderr;
89 
90 	struct lws 	*wsi_stdout;
91 	struct lws 	*wsi_stderr;
92 
93 	uint32_t	pty_in_bloat_nl_to_crnl:1;
94 	uint32_t	pty_in_echo:1;
95 	uint32_t	pty_in_cr_to_nl:1;
96 
97 	uint32_t	insert_lf:1;
98 };
99 
100 
101 /* ops: channel lifecycle */
102 
103 static int
ssh_ops_channel_create(struct lws * wsi,void ** _priv)104 ssh_ops_channel_create(struct lws *wsi, void **_priv)
105 {
106 	struct sshd_instance_priv *priv;
107 
108 	priv = malloc(sizeof(struct sshd_instance_priv));
109 	*_priv = priv;
110 	if (!priv)
111 		return 1;
112 
113 	memset(priv, 0, sizeof(*priv));
114 
115 	priv->ring_stdout = lws_ring_create(1, 1024, NULL);
116 	if (!priv->ring_stdout) {
117 		free(priv);
118 
119 		return 1;
120 	}
121 
122 	priv->ring_stderr = lws_ring_create(1, 1024, NULL);
123 	if (!priv->ring_stderr) {
124 		lws_ring_destroy(priv->ring_stdout);
125 		free(priv);
126 
127 		return 1;
128 	}
129 
130 	return 0;
131 }
132 
133 static int
ssh_ops_channel_destroy(void * _priv)134 ssh_ops_channel_destroy(void *_priv)
135 {
136 	struct sshd_instance_priv *priv = _priv;
137 	const struct lws_protocol_vhost_options *pvo = priv->env, *pvo1;
138 
139 	while (pvo) {
140 		pvo1 = pvo;
141 		free((char *)pvo->name);
142 		free((char *)pvo->value);
143 		pvo = pvo->next;
144 		free((void *)pvo1);
145 	}
146 	priv->env = NULL;
147 
148 	lws_ring_destroy(priv->ring_stdout);
149 	lws_ring_destroy(priv->ring_stderr);
150 	free(priv);
151 
152 	return 0;
153 }
154 
155 /* ops: IO */
156 
157 static int
ssh_ops_tx_waiting(void * _priv)158 ssh_ops_tx_waiting(void *_priv)
159 {
160 	struct sshd_instance_priv *priv = _priv;
161 	int s = 0;
162 
163 	if (lws_ring_get_count_waiting_elements(priv->ring_stdout, NULL))
164 		s |= LWS_STDOUT;
165 	if (lws_ring_get_count_waiting_elements(priv->ring_stderr, NULL))
166 		s |= LWS_STDERR;
167 
168 	return s;
169 }
170 
171 static size_t
ssh_ops_tx(void * _priv,int stdch,uint8_t * buf,size_t len)172 ssh_ops_tx(void *_priv, int stdch, uint8_t *buf, size_t len)
173 {
174 	struct sshd_instance_priv *priv = _priv;
175 	struct lws_ring *r;
176 	struct lws *wsi;
177 	size_t n;
178 
179 	if (stdch == LWS_STDOUT) {
180 		r = priv->ring_stdout;
181 		wsi = priv->wsi_stdout;
182 	} else {
183 		r = priv->ring_stderr;
184 		wsi = priv->wsi_stderr;
185 	}
186 
187 	n = lws_ring_consume(r, NULL, buf, len);
188 
189 	if (n)
190 		lws_rx_flow_control(wsi, 1);
191 
192 	return n;
193 }
194 
195 
196 static int
ssh_ops_rx(void * _priv,struct lws * wsi,const uint8_t * buf,uint32_t len)197 ssh_ops_rx(void *_priv, struct lws *wsi, const uint8_t *buf, uint32_t len)
198 {
199 	struct sshd_instance_priv *priv = _priv;
200 	struct lws *wsi_stdin = lws_cgi_get_stdwsi(wsi, LWS_STDIN);
201 	int fd;
202 	uint8_t bbuf[256];
203 
204 	if (!wsi_stdin)
205 		return -1;
206 
207 	fd = lws_get_socket_fd(wsi_stdin);
208 
209 	if (*buf != 0x0d) {
210 		if (write(fd, buf, len) != (int)len)
211 			return -1;
212 		if (priv->pty_in_echo) {
213 			if (!lws_ring_insert(priv->ring_stdout, buf, 1))
214 				lwsl_notice("dropping...\n");
215 			lws_callback_on_writable(wsi);
216 		}
217 	} else {
218 		bbuf[0] = 0x0a;
219 		bbuf[1] = 0x0a;
220 		if (write(fd, bbuf, 1) != 1)
221 			return -1;
222 
223 		if (priv->pty_in_echo) {
224 			bbuf[0] = 0x0d;
225 			bbuf[1] = 0x0a;
226 			if (!lws_ring_insert(priv->ring_stdout, bbuf, 2))
227 				lwsl_notice("dropping...\n");
228 			lws_callback_on_writable(wsi);
229 		}
230 	}
231 
232 	return 0;
233 }
234 
235 /* ops: storage for the (autogenerated) persistent server key */
236 
237 static size_t
ssh_ops_get_server_key(struct lws * wsi,uint8_t * buf,size_t len)238 ssh_ops_get_server_key(struct lws *wsi, uint8_t *buf, size_t len)
239 {
240 	int fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY), n;
241 
242 	if (fd == -1) {
243 		lwsl_err("%s: unable to open %s for read: %s\n", __func__,
244 				TEST_SERVER_KEY_PATH, strerror(errno));
245 
246 		return 0;
247 	}
248 
249 	n = (int)read(fd, buf, len);
250 	if (n < 0) {
251 		lwsl_err("%s: read failed: %d\n", __func__, n);
252 		n = 0;
253 	}
254 
255 	close(fd);
256 
257 	return (size_t)n;
258 }
259 
260 static size_t
ssh_ops_set_server_key(struct lws * wsi,uint8_t * buf,size_t len)261 ssh_ops_set_server_key(struct lws *wsi, uint8_t *buf, size_t len)
262 {
263 	int fd = lws_open(TEST_SERVER_KEY_PATH, O_CREAT | O_TRUNC | O_RDWR, 0600);
264 	int n;
265 
266 	lwsl_notice("%s: %d\n", __func__, fd);
267 	if (fd == -1) {
268 		lwsl_err("%s: unable to open %s for write: %s\n", __func__,
269 				TEST_SERVER_KEY_PATH, strerror(errno));
270 
271 		return 0;
272 	}
273 
274 	n = (int)write(fd, buf, len);
275 	if (n < 0) {
276 		lwsl_err("%s: read failed: %d\n", __func__, errno);
277 		n = 0;
278 	}
279 
280 	close(fd);
281 
282 	return (size_t)n;
283 }
284 
285 /* ops: auth */
286 
287 static int
ssh_ops_is_pubkey_authorized(const char * username,const char * type,const uint8_t * peer,int peer_len)288 ssh_ops_is_pubkey_authorized(const char *username, const char *type,
289 				 const uint8_t *peer, int peer_len)
290 {
291 	char *aps, *p, *ps;
292 	int n = (int)strlen(type), alen = 2048, ret = 2, len;
293 	size_t s = 0;
294 
295 	lwsl_info("%s: checking pubkey for %s\n", __func__, username);
296 
297 	s = strlen(authorized_key) + 1;
298 
299 	aps = malloc(s);
300 	if (!aps) {
301 		lwsl_notice("OOM 1\n");
302 		goto bail_p1;
303 	}
304 	memcpy(aps, authorized_key, s);
305 
306 	/* this is all we understand at the moment */
307 	if (strcmp(type, "ssh-rsa")) {
308 		lwsl_notice("type is not ssh-rsa\n");
309 		goto bail_p1;
310 	}
311 	p = aps;
312 
313 	if (strncmp(p, type, (unsigned int)n)) {
314 		lwsl_notice("lead-in string  does not match %s\n", type);
315 		goto bail_p1;
316 	}
317 
318 	p += n;
319 	if (*p != ' ') {
320 		lwsl_notice("missing space at end of lead-in\n");
321 		goto bail_p1;
322 	}
323 
324 
325 	p++;
326 	ps = malloc((unsigned int)alen);
327 	if (!ps) {
328 		lwsl_notice("OOM 2\n");
329 		free(aps);
330 		goto bail;
331 	}
332 	len = lws_b64_decode_string(p, ps, alen);
333 	free(aps);
334 	if (len < 0) {
335 		lwsl_notice("key too big\n");
336 		goto bail;
337 	}
338 
339 	if (peer_len > len) {
340 		lwsl_notice("peer_len %d bigger than decoded len %d\n",
341 				peer_len, len);
342 		goto bail;
343 	}
344 
345 	/*
346 	 * once we are past that, it's the same <len32>name
347 	 * <len32>E<len32>N that the peer sends us
348 	 */
349 
350 	if (lws_timingsafe_bcmp(peer, ps, (uint32_t)peer_len)) {
351 		lwsl_info("factors mismatch\n");
352 		goto bail;
353 	}
354 
355 	lwsl_info("pubkey authorized\n");
356 
357 	ret = 0;
358 bail:
359 	free(ps);
360 
361 	return ret;
362 
363 bail_p1:
364 	if (aps)
365 		free(aps);
366 
367 	return 1;
368 }
369 
370 /* ops: spawn subprocess */
371 
372 static int
ssh_cgi_env_add(struct sshd_instance_priv * priv,const char * name,const char * value)373 ssh_cgi_env_add(struct sshd_instance_priv *priv, const char *name,
374 		const char *value)
375 {
376 	struct lws_protocol_vhost_options *pvo = malloc(sizeof(*pvo));
377 
378 	if (!pvo)
379 		return 1;
380 
381 	pvo->name = malloc(strlen(name) + 1);
382 	if (!pvo->name) {
383 		free(pvo);
384 		return 1;
385 	}
386 
387 	pvo->value = malloc(strlen(value) + 1);
388 	if (!pvo->value) {
389 		free((char *)pvo->name);
390 		free(pvo);
391 		return 1;
392 	}
393 
394 	strcpy((char *)pvo->name, name);
395 	strcpy((char *)pvo->value, value);
396 
397 	pvo->next = priv->env;
398 	priv->env = pvo;
399 
400 	lwsl_notice("%s: ENV %s <- %s\n", __func__, name, value);
401 
402 	return 0;
403 }
404 
405 static int
ssh_ops_set_env(void * _priv,const char * name,const char * value)406 ssh_ops_set_env(void *_priv, const char *name, const char *value)
407 {
408 	struct sshd_instance_priv *priv = _priv;
409 
410 	return ssh_cgi_env_add(priv, name, value);
411 }
412 
413 
414 static int
ssh_ops_pty_req(void * _priv,struct lws_ssh_pty * pty)415 ssh_ops_pty_req(void *_priv, struct lws_ssh_pty *pty)
416 {
417 	struct sshd_instance_priv *priv = _priv;
418 	uint8_t *p = (uint8_t *)pty->modes, opc;
419 	uint32_t arg;
420 
421 	lwsl_notice("%s: pty term %s, modes_len %d\n", __func__, pty->term,
422 		    pty->modes_len);
423 
424 	ssh_cgi_env_add(priv, "TERM", pty->term);
425 
426 	while (p < (uint8_t *)pty->modes + pty->modes_len) {
427 		if (*p >= 160)
428 			break;
429 		if (!*p)
430 			break;
431 		opc = *p++;
432 
433 		arg = (uint32_t)(*p++ << 24);
434 		arg |= (uint32_t)(*p++ << 16);
435 		arg |= (uint32_t)(*p++ << 8);
436 		arg |= (uint32_t)(*p++);
437 
438 		lwsl_debug("pty opc %d: 0x%x\n", opc, arg);
439 
440 		switch (opc) {
441 		case SSHMO_ICRNL:
442 			priv->pty_in_cr_to_nl = !!arg;
443 			lwsl_notice(" SSHMO_ICRNL: %d\n", !!arg);
444 			break;
445 		case SSHMO_ONLCR:
446 			priv->pty_in_bloat_nl_to_crnl = !!arg;
447 			lwsl_notice(" SSHMO_ONLCR: %d\n", !!arg);
448 			break;
449 		case SSHMO_ECHO:
450 //			priv->pty_in_echo = !!arg;
451 			lwsl_notice(" SSHMO_ECHO: %d\n", !!arg);
452 			break;
453 		}
454 	}
455 
456 	return 0;
457 }
458 
459 static int
ssh_ops_child_process_io(void * _priv,struct lws * wsi,struct lws_cgi_args * args)460 ssh_ops_child_process_io(void *_priv, struct lws *wsi,
461 			 struct lws_cgi_args *args)
462 {
463 	struct sshd_instance_priv *priv = _priv;
464 	struct lws_ring *r = priv->ring_stdout;
465 	void *rp;
466 	size_t bytes;
467 	int n, m;
468 
469 	priv->wsi_stdout = args->stdwsi[LWS_STDOUT];
470 	priv->wsi_stderr = args->stdwsi[LWS_STDERR];
471 
472 	switch (args->ch) {
473 	case LWS_STDIN:
474 		lwsl_notice("STDIN\n");
475 		break;
476 
477 	case LWS_STDERR:
478 		r = priv->ring_stderr;
479 		/* fallthru */
480 	case LWS_STDOUT:
481 		if (lws_ring_next_linear_insert_range(r, &rp, &bytes) ||
482 		    bytes < 1) {
483 			lwsl_notice("bytes %d\n", (int)bytes);
484 			/* no room in the fifo */
485 			break;
486 		}
487 		if (priv->pty_in_bloat_nl_to_crnl) {
488 			uint8_t buf[256], *p, *d;
489 
490 			if (bytes != 1)
491 				n = (int)(bytes / 2);
492 			else
493 				n = 1;
494 			if (n > (int)sizeof(buf))
495 				n = sizeof(buf);
496 
497 			if (!n)
498 				break;
499 
500 			m = lws_get_socket_fd(args->stdwsi[args->ch]);
501 			if (m < 0)
502 				return -1;
503 			n = (int)read(m, buf, (unsigned int)n);
504 			if (n < 0)
505 				return -1;
506 			if (n == 0) {
507 				lwsl_notice("zero length stdin %d\n", n);
508 				break;
509 			}
510 			m = 0;
511 			p = rp;
512 			d = buf;
513 			while (m++ < n) {
514 				if (priv->insert_lf) {
515 					priv->insert_lf = 0;
516 					*p++ = 0x0d;
517 				}
518 				if (*d == 0x0a)
519 					priv->insert_lf = 1;
520 
521 				*p++ = *d++;
522 			}
523 			n = lws_ptr_diff((void *)p, rp);
524 			if (n < (int)bytes && priv->insert_lf) {
525 				priv->insert_lf = 0;
526 				*p++ = 0x0d;
527 				n++;
528 			}
529 		} else {
530 			n = lws_get_socket_fd(args->stdwsi[args->ch]);
531 			if (n < 0)
532 				return -1;
533 			n = (int)read(n, rp, bytes);
534 			if (n < 0)
535 				return -1;
536 		}
537 
538 		lws_rx_flow_control(args->stdwsi[args->ch], 0);
539 
540 		lws_ring_bump_head(r, (unsigned int)n);
541 		lws_callback_on_writable(wsi);
542 		break;
543 	}
544 
545 	return 0;
546 }
547 
548 static int
ssh_ops_child_process_terminated(void * priv,struct lws * wsi)549 ssh_ops_child_process_terminated(void *priv, struct lws *wsi)
550 {
551 	lwsl_notice("%s\n", __func__);
552 	return -1;
553 }
554 
555 static int
ssh_ops_exec(void * _priv,struct lws * wsi,const char * command,lws_ssh_finish_exec finish,void * finish_handle)556 ssh_ops_exec(void *_priv, struct lws *wsi, const char *command, lws_ssh_finish_exec finish, void *finish_handle)
557 {
558 	lwsl_notice("%s: EXEC %s\n", __func__, command);
559 
560 	/* we don't want to exec anything */
561 	return 1;
562 }
563 
564 static int
ssh_ops_shell(void * _priv,struct lws * wsi,lws_ssh_finish_exec finish,void * finish_handle)565 ssh_ops_shell(void *_priv, struct lws *wsi, lws_ssh_finish_exec finish, void *finish_handle)
566 {
567 	struct sshd_instance_priv *priv = _priv;
568 	const char *cmd[] = {
569 		"/bin/bash",
570 		"-i",
571 		"-l",
572 		NULL
573 	};
574 	lwsl_notice("%s: SHELL\n", __func__);
575 
576 	if (lws_cgi(wsi, cmd, -1, 0, priv->env)) {
577 		lwsl_notice("shell spawn failed\n");
578 		return -1;
579 	}
580 
581 	return 0;
582 }
583 
584 /* ops: banner */
585 
586 static size_t
ssh_ops_banner(char * buf,size_t max_len,char * lang,size_t max_lang_len)587 ssh_ops_banner(char *buf, size_t max_len, char *lang, size_t max_lang_len)
588 {
589 	int n = lws_snprintf(buf, max_len, "\n"
590 		      " |\\---/|  lws-ssh Test Server\n"
591 		      " | o_o |  SSH Terminal Server\n"
592 		      "  \\_^_/   Copyright (C) 2017-2020 Crash Barrier Ltd\n\n");
593 
594 	lws_snprintf(lang, max_lang_len, "en/US");
595 
596 	return (size_t)n;
597 }
598 
599 static void
ssh_ops_disconnect_reason(uint32_t reason,const char * desc,const char * desc_lang)600 ssh_ops_disconnect_reason(uint32_t reason, const char *desc,
601 			  const char *desc_lang)
602 {
603 	lwsl_notice("DISCONNECT reason 0x%X, %s (lang %s)\n", reason, desc,
604 			desc_lang);
605 }
606 
607 static const struct lws_ssh_ops ssh_ops = {
608 	.channel_create			= ssh_ops_channel_create,
609 	.channel_destroy		= ssh_ops_channel_destroy,
610 	.tx_waiting			= ssh_ops_tx_waiting,
611 	.tx				= ssh_ops_tx,
612 	.rx				= ssh_ops_rx,
613 	.get_server_key			= ssh_ops_get_server_key,
614 	.set_server_key			= ssh_ops_set_server_key,
615 	.set_env			= ssh_ops_set_env,
616 	.pty_req			= ssh_ops_pty_req,
617 	.child_process_io		= ssh_ops_child_process_io,
618 	.child_process_terminated	= ssh_ops_child_process_terminated,
619 	.exec				= ssh_ops_exec,
620 	.shell				= ssh_ops_shell,
621 	.is_pubkey_authorized		= ssh_ops_is_pubkey_authorized,
622 	.banner				= ssh_ops_banner,
623 	.disconnect_reason		= ssh_ops_disconnect_reason,
624 	.server_string			= "SSH-2.0-Libwebsockets",
625 	.api_version			= 2,
626 };
627 
628 /*
629  * use per-vhost options to bind the ops struct to the instance of the
630  * "lws_raw_sshd" protocol instantiated on our vhost
631  */
632 
633 static const struct lws_protocol_vhost_options pvo_ssh_ops = {
634 	NULL,
635 	NULL,
636 	"ops",
637 	(void *)&ssh_ops
638 };
639 
640 static const struct lws_protocol_vhost_options pvo_ssh = {
641 	NULL,
642 	&pvo_ssh_ops,
643 	"lws-ssh-base",
644 	"" /* ignored, just matches the protocol name above */
645 };
646 
sighandler(int sig)647 void sighandler(int sig)
648 {
649 	force_exit = 1;
650 	lws_cancel_service(context);
651 }
652 
653 static int
callback_lws_sshd_demo(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)654 callback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason,
655 		       void *user, void *in, size_t len)
656 {
657 	struct per_vhost_data__lws_sshd_demo *vhd =
658 			(struct per_vhost_data__lws_sshd_demo *)
659 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
660 						 lws_get_protocol(wsi));
661 
662 	switch (reason) {
663 	case LWS_CALLBACK_PROTOCOL_INIT:
664 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
665 						  lws_get_protocol(wsi),
666 				sizeof(struct per_vhost_data__lws_sshd_demo));
667 		if (!vhd)
668 			return 0;
669 		/*
670 		 * During this we still have the privs / caps we were started
671 		 * with.  So open an fd on the server key, either just for read
672 		 * or for creat / trunc if doesn't exist.  This allows us to
673 		 * deal with it down /etc/.. when just after this we will lose
674 		 * the privileges needed to read / write /etc/...
675 		 */
676 		vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH, O_RDONLY);
677 		if (vhd->privileged_fd == -1)
678 			vhd->privileged_fd = lws_open(TEST_SERVER_KEY_PATH,
679 					O_CREAT | O_TRUNC | O_RDWR, 0600);
680 		if (vhd->privileged_fd == -1) {
681 			lwsl_warn("%s: Can't open %s\n", __func__,
682 				 TEST_SERVER_KEY_PATH);
683 			return 0;
684 		}
685 		break;
686 
687 	case LWS_CALLBACK_PROTOCOL_DESTROY:
688 		if (vhd)
689 			close(vhd->privileged_fd);
690 		break;
691 
692 	case LWS_CALLBACK_VHOST_CERT_AGING:
693 		break;
694 
695 	case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
696 		break;
697 
698 	default:
699 		if (!vhd->ssh_base_protocol) {
700 			vhd->ssh_base_protocol = lws_vhost_name_to_protocol(
701 							lws_get_vhost(wsi),
702 							"lws-ssh-base");
703 			if (vhd->ssh_base_protocol)
704 				user = lws_adjust_protocol_psds(wsi,
705 				vhd->ssh_base_protocol->per_session_data_size);
706 		}
707 
708 		if (vhd->ssh_base_protocol)
709 			return vhd->ssh_base_protocol->callback(wsi, reason,
710 								user, in, len);
711 		else
712 			lwsl_notice("can't find lws-ssh-base\n");
713 		break;
714 	}
715 
716 	return 0;
717 }
718 
719 
720 const struct lws_protocols lws_sshd_demo_protocols[] = {
721 	{
722 		"lws-sshd-demo",
723 		callback_lws_sshd_demo,
724 		0,
725 		1024, /* rx buf size must be >= permessage-deflate rx size */
726 		0, (void *)&ssh_ops, 0
727 	}
728 
729 };
730 
731 
main()732 int main()
733 {
734 	static struct lws_context_creation_info info;
735 	struct lws_vhost *vh_sshd;
736 	int ret = 1, n;
737 
738 	/* info is on the stack, it must be cleared down before use */
739 	memset(&info, 0, sizeof(info));
740 
741 	signal(SIGINT, sighandler);
742 	lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE
743 			/*| LLL_INFO */
744 			/* | LLL_DEBUG */, NULL);
745 
746 	lwsl_notice("lws test-sshd -- Copyright (C) 2017 <[email protected]>\n");
747 
748 	/* create the lws context */
749 
750 	info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
751 		       LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
752 
753 	context = lws_create_context(&info);
754 	if (!context) {
755 		lwsl_err("Failed to create context\n");
756 		return 1;
757 	}
758 
759 	/* create our listening vhost */
760 
761 	info.port = 2200;
762 	info.options = LWS_SERVER_OPTION_ONLY_RAW;
763 	info.vhost_name = "sshd";
764 	info.protocols = lws_sshd_demo_protocols;
765 	info.pvo = &pvo_ssh;
766 
767 	vh_sshd = lws_create_vhost(context, &info);
768 	if (!vh_sshd) {
769 		lwsl_err("Failed to create sshd vhost\n");
770 		goto bail;
771 	}
772 
773 	/* spin doing service */
774 
775 	n = 0;
776 	while (!n  && !force_exit)
777 		n = lws_service(context, 0);
778 
779 	ret = 0;
780 
781 	/* cleanup */
782 
783 bail:
784 	lws_context_destroy(context);
785 	lwsl_notice("exiting...\n");
786 
787 	return ret;
788 }
789