• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include "private-lwsgs.h"
26 #include <stdlib.h>
27 
28 /* keep changes in sync with the enum in lwsgs.h */
29 static const char * const param_names[] = {
30 	"username",
31 	"password",
32 	"password2",
33 	"email",
34 	"register",
35 	"good",
36 	"bad",
37 	"reg-good",
38 	"reg-bad",
39 	"admin",
40 	"forgot",
41 	"forgot-good",
42 	"forgot-bad",
43 	"forgot-post-good",
44 	"forgot-post-bad",
45 	"change",
46 	"curpw",
47 	"delete"
48 };
49 
50 struct lwsgs_fill_args {
51 	char *buf;
52 	int len;
53 };
54 
55 static const struct lws_protocols protocols[];
56 
57 struct lwsgs_subst_args
58 {
59 	struct per_session_data__gs *pss;
60 	struct per_vhost_data__gs *vhd;
61 	struct lws *wsi;
62 };
63 
64 static const char *
lwsgs_subst(void * data,int index)65 lwsgs_subst(void *data, int index)
66 {
67 	struct lwsgs_subst_args *a = (struct lwsgs_subst_args *)data;
68 	struct lwsgs_user u;
69 	lwsgw_hash sid;
70 	char esc[96], s[100];
71 	int n;
72 
73 	a->pss->result[0] = '\0';
74 	u.email[0] = '\0';
75 	if (!lwsgs_get_sid_from_wsi(a->wsi, &sid)) {
76 		if (lwsgs_lookup_session(a->vhd, &sid, a->pss->result, 31)) {
77 			lwsl_notice("sid lookup for %s failed\n", sid.id);
78 			a->pss->delete_session = sid;
79 			return NULL;
80 		}
81 		lws_snprintf(s, sizeof(s) - 1, "select username,email "
82 			 "from users where username = '%s';",
83 			 lws_sql_purify(esc, a->pss->result, sizeof(esc) - 1));
84 		if (sqlite3_exec(a->vhd->pdb, s, lwsgs_lookup_callback_user,
85 				 &u, NULL) != SQLITE_OK) {
86 			lwsl_err("Unable to lookup token: %s\n",
87 				 sqlite3_errmsg(a->vhd->pdb));
88 			a->pss->delete_session = sid;
89 			return NULL;
90 		}
91 	} else
92 		lwsl_notice("no sid\n");
93 
94 	lws_strncpy(a->pss->result + 32, u.email, 100);
95 
96 	switch (index) {
97 	case 0:
98 		return a->pss->result;
99 
100 	case 1:
101 		n = lwsgs_get_auth_level(a->vhd, a->pss->result);
102 		sprintf(a->pss->result, "%d", n);
103 		return a->pss->result;
104 	case 2:
105 		return a->pss->result + 32;
106 	}
107 
108 	return NULL;
109 }
110 
111 static int
lws_get_effective_host(struct lws * wsi,char * buf,size_t buflen)112 lws_get_effective_host(struct lws *wsi, char *buf, size_t buflen)
113 {
114 #if defined(LWS_ROLE_H2)
115 	/* h2 */
116 	if (lws_hdr_copy(wsi, buf, buflen - 1,
117 			 WSI_TOKEN_HTTP_COLON_AUTHORITY) > 0)
118 		return 0;
119 #endif
120 	/* h1 */
121 	if (lws_hdr_copy(wsi, buf, buflen - 1,  WSI_TOKEN_HOST) > 0)
122 		return 0;
123 
124 	return 1;
125 }
126 
127 static int
callback_generic_sessions(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)128 callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
129 			  void *user, void *in, size_t len)
130 {
131 	struct per_session_data__gs *pss = (struct per_session_data__gs *)user;
132 	const struct lws_protocol_vhost_options *pvo;
133 	struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)
134 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
135 				lws_vhost_name_to_protocol(lws_get_vhost(wsi),
136 						"protocol-generic-sessions"));
137 	char cookie[1024], username[32], *pc = cookie;
138 	unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
139 	struct lws_process_html_args *args = in;
140 	struct lws_session_info *sinfo;
141 	char s[LWSGS_EMAIL_CONTENT_SIZE];
142 	unsigned char *p, *start, *end;
143 	const char *cp, *cp1;
144 	sqlite3_stmt *sm;
145 	lwsgw_hash sid;
146 #if defined(LWS_WITH_SMTP)
147 	lws_abs_t abs;
148 #endif
149 	int n;
150 
151 	switch (reason) {
152 	case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
153 
154 		vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
155 			lws_get_protocol(wsi), sizeof(struct per_vhost_data__gs));
156 		if (!vhd)
157 			return 1;
158 		vhd->context = lws_get_context(wsi);
159 
160 		/* defaults */
161 		vhd->timeout_idle_secs = 600;
162 		vhd->timeout_absolute_secs = 36000;
163 		vhd->timeout_anon_absolute_secs = 1200;
164 		vhd->timeout_email_secs = 24 * 3600;
165 
166 
167 		strcpy(vhd->helo, "unconfigured.com");
168 		strcpy(vhd->ip, "127.0.0.1");
169 		strcpy(vhd->email_from, "noreply@unconfigured.com");
170 		strcpy(vhd->email_title, "Registration Email from unconfigured");
171 		vhd->urlroot[0] = '\0';
172 
173 		pvo = (const struct lws_protocol_vhost_options *)in;
174 		while (pvo) {
175 			if (!strcmp(pvo->name, "admin-user"))
176 				lws_strncpy(vhd->admin_user, pvo->value,
177 					sizeof(vhd->admin_user));
178 			if (!strcmp(pvo->name, "urlroot"))
179 				lws_strncpy(vhd->urlroot, pvo->value,
180 					sizeof(vhd->urlroot));
181 			if (!strcmp(pvo->name, "admin-password-sha256"))
182 				lws_strncpy(vhd->admin_password_sha256.id, pvo->value,
183 					sizeof(vhd->admin_password_sha256.id));
184 			if (!strcmp(pvo->name, "session-db"))
185 				lws_strncpy(vhd->session_db, pvo->value,
186 					sizeof(vhd->session_db));
187 			if (!strcmp(pvo->name, "confounder"))
188 				lws_strncpy(vhd->confounder, pvo->value,
189 					sizeof(vhd->confounder));
190 			if (!strcmp(pvo->name, "email-from"))
191 				lws_strncpy(vhd->email_from, pvo->value,
192 					sizeof(vhd->email_from));
193 			if (!strcmp(pvo->name, "email-helo"))
194 				lws_strncpy(vhd->helo, pvo->value, sizeof(vhd->helo));
195 			if (!strcmp(pvo->name, "email-template"))
196 				lws_strncpy(vhd->email_template, pvo->value,
197 					sizeof(vhd->email_template));
198 			if (!strcmp(pvo->name, "email-title"))
199 				lws_strncpy(vhd->email_title, pvo->value,
200 					sizeof(vhd->email_title));
201 			if (!strcmp(pvo->name, "email-contact-person"))
202 				lws_strncpy(vhd->email_contact_person, pvo->value,
203 					sizeof(vhd->email_contact_person));
204 			if (!strcmp(pvo->name, "email-confirm-url-base"))
205 				lws_strncpy(vhd->email_confirm_url, pvo->value,
206 					sizeof(vhd->email_confirm_url));
207 			if (!strcmp(pvo->name, "email-server-ip"))
208 				lws_strncpy(vhd->ip, pvo->value, sizeof(vhd->ip));
209 
210 			if (!strcmp(pvo->name, "timeout-idle-secs"))
211 				vhd->timeout_idle_secs = atoi(pvo->value);
212 			if (!strcmp(pvo->name, "timeout-absolute-secs"))
213 				vhd->timeout_absolute_secs = atoi(pvo->value);
214 			if (!strcmp(pvo->name, "timeout-anon-absolute-secs"))
215 				vhd->timeout_anon_absolute_secs = atoi(pvo->value);
216 			if (!strcmp(pvo->name, "email-expire"))
217 				vhd->timeout_email_secs = atoi(pvo->value);
218 			pvo = pvo->next;
219 		}
220 		if (!vhd->admin_user[0] ||
221 		    !vhd->admin_password_sha256.id[0] ||
222 		    !vhd->session_db[0]) {
223 			lwsl_err("generic-sessions: "
224 				 "You must give \"admin-user\", "
225 				 "\"admin-password-sha256\", "
226 				 "and \"session_db\" per-vhost options\n");
227 			return 1;
228 		}
229 
230 		if (lws_struct_sq3_open(lws_get_context(wsi),
231 					vhd->session_db, &vhd->pdb)) {
232 			lwsl_err("Unable to open session db %s: %s\n",
233 				 vhd->session_db, sqlite3_errmsg(vhd->pdb));
234 
235 			return 1;
236 		}
237 
238 		if (sqlite3_prepare(vhd->pdb,
239 				    "create table if not exists sessions ("
240 				    " name char(65),"
241 				    " username varchar(32),"
242 				    " expire integer"
243 				    ");",
244 				    -1, &sm, NULL) != SQLITE_OK) {
245 			lwsl_err("Unable to prepare session table init: %s\n",
246 				 sqlite3_errmsg(vhd->pdb));
247 
248 			return 1;
249 		}
250 
251 		if (sqlite3_step(sm) != SQLITE_DONE) {
252 			lwsl_err("Unable to run session table init: %s\n",
253 				 sqlite3_errmsg(vhd->pdb));
254 
255 			return 1;
256 		}
257 		sqlite3_finalize(sm);
258 
259 		if (sqlite3_exec(vhd->pdb,
260 				 "create table if not exists users ("
261 				 " username varchar(32),"
262 				 " creation_time integer,"
263 				 " ip varchar(46),"
264 				 " email varchar(100),"
265 				 " pwhash varchar(65),"
266 				 " pwsalt varchar(65),"
267 				 " pwchange_time integer,"
268 				 " token varchar(65),"
269 				 " verified integer,"
270 				 " token_time integer,"
271 				 " last_forgot_validated integer,"
272 				 " primary key (username)"
273 				 ");",
274 				 NULL, NULL, NULL) != SQLITE_OK) {
275 			lwsl_err("Unable to create user table: %s\n",
276 				 sqlite3_errmsg(vhd->pdb));
277 
278 			return 1;
279 		}
280 
281 #if defined(LWS_WITH_SMTP)
282 
283 		memset(&abs, 0, sizeof(abs));
284 		abs.vh = lws_get_vhost(wsi);
285 
286 		/* select the protocol and bind its tokens */
287 
288 		abs.ap = lws_abs_protocol_get_by_name("smtp");
289 		if (!abs.ap)
290 			return 1;
291 
292 		vhd->protocol_tokens[0].name_index = LTMI_PSMTP_V_HELO;
293 		vhd->protocol_tokens[0].u.value = vhd->helo;
294 
295 		abs.ap_tokens = vhd->protocol_tokens;
296 
297 		/* select the transport and bind its tokens */
298 
299 		abs.at = lws_abs_transport_get_by_name("raw_skt");
300 		if (!abs.at)
301 			return 1;
302 
303 		vhd->transport_tokens[0].name_index = LTMI_PEER_V_DNS_ADDRESS;
304 		vhd->transport_tokens[0].u.value = vhd->ip;
305 		vhd->transport_tokens[1].name_index = LTMI_PEER_LV_PORT;
306 		vhd->transport_tokens[1].u.lvalue = 25;
307 
308 		abs.at_tokens = vhd->transport_tokens;
309 
310 		vhd->smtp_client = lws_abs_bind_and_create_instance(&abs);
311 		if (!vhd->smtp_client)
312 			return 1;
313 
314 		lwsl_notice("%s: created SMTP client\n", __func__);
315 #endif
316 		break;
317 
318 	case LWS_CALLBACK_PROTOCOL_DESTROY:
319 	//	lwsl_notice("gs: LWS_CALLBACK_PROTOCOL_DESTROY: v=%p, ctx=%p\n", vhd, vhd->context);
320 		if (vhd->pdb) {
321 			sqlite3_close(vhd->pdb);
322 			vhd->pdb = NULL;
323 		}
324 #if defined(LWS_WITH_SMTP)
325 		if (vhd->smtp_client)
326 			lws_abs_destroy_instance(&vhd->smtp_client);
327 #endif
328 		break;
329 
330 	case LWS_CALLBACK_HTTP_WRITEABLE:
331                 if (!pss->check_response)
332                         break;
333                 pss->check_response = 0;
334 		n = lws_write(wsi, (unsigned char *)&pss->check_response_value,
335 				1, LWS_WRITE_HTTP | LWS_WRITE_H2_STREAM_END);
336 		if (n != 1)
337 			return -1;
338 		goto try_to_reuse;
339 
340 	case LWS_CALLBACK_HTTP:
341 		if (!pss) {
342 			lwsl_err("%s: no valid pss\n", __func__);
343 			return 1;
344 		}
345 
346 		pss->login_session.id[0] = '\0';
347 		pss->phs.pos = 0;
348 
349 		cp = in;
350 		if ((*(const char *)in == '/'))
351 			cp++;
352 
353 		if (lws_get_effective_host(wsi, cookie, sizeof(cookie))) {
354 			lwsl_err("%s: HTTP: no effective host\n", __func__);
355 			return 1;
356 		}
357 
358 		lwsl_notice("LWS_CALLBACK_HTTP: %s, HOST '%s'\n",
359 				(const char *)in, cookie);
360 
361 		n = strlen(cp);
362 
363 		lws_snprintf(pss->onward, sizeof(pss->onward),
364 			     "%s%s", vhd->urlroot, (const char *)in);
365 
366 		if (n >= 12 &&
367 		    !strcmp(cp + n - 12, "lwsgs-forgot")) {
368 			lwsgs_handler_forgot(vhd, wsi, pss);
369 			goto redirect_with_cookie;
370 		}
371 
372 		if (n >= 13 &&
373 		    !strcmp(cp + n - 13, "lwsgs-confirm")) {
374 			lwsgs_handler_confirm(vhd, wsi, pss);
375 			goto redirect_with_cookie;
376 		}
377 		cp1 = strstr(cp, "lwsgs-check/");
378 		if (cp1) {
379 			lwsgs_handler_check(vhd, wsi, pss, cp1 + 12);
380 			/* second, async part will complete transaction */
381 			break;
382 		}
383 
384 		if (n >= 11 && cp && !strcmp(cp + n - 11, "lwsgs-login"))
385 			break;
386 		if (n >= 12 && cp && !strcmp(cp + n - 12, "lwsgs-logout"))
387 			break;
388 		if (n >= 12 && cp && !strcmp(cp + n - 12, "lwsgs-forgot"))
389 			break;
390 		if (n >= 12 && cp && !strcmp(cp + n - 12, "lwsgs-change"))
391 			break;
392 
393 		/* if no legitimate url for GET, return 404 */
394 
395 		lwsl_err("%s: http doing 404 on %s\n", __func__, cp ? cp : "null");
396 		lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
397 
398 		return -1;
399 		//goto try_to_reuse;
400 
401 	case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
402 		args = (struct lws_process_html_args *)in;
403 		if (!args->chunked)
404 			break;
405 	case LWS_CALLBACK_CHECK_ACCESS_RIGHTS:
406 		n = 0;
407 		username[0] = '\0';
408 		sid.id[0] = '\0';
409 		args = (struct lws_process_html_args *)in;
410 		lwsl_notice("%s: LWS_CALLBACK_CHECK_ACCESS_RIGHTS: need 0x%x\n",
411 				__func__, args->max_len);
412 		if (!lwsgs_get_sid_from_wsi(wsi, &sid)) {
413 			if (lwsgs_lookup_session(vhd, &sid, username,
414 						 sizeof(username))) {
415 
416 				/*
417 				 * if we're authenticating for ws, we don't
418 				 * want to redirect it or gain a cookie on that,
419 				 * he'll need to get the cookie from http
420 				 * interactions outside of this.
421 				 */
422 				if (args->chunked) {
423 					lwsl_notice("%s: ws auth failed\n",
424 							__func__);
425 
426 					return 1;
427 				}
428 
429 				lwsl_notice("session lookup for %s failed, "
430 					    "probably expired\n", sid.id);
431 				pss->delete_session = sid;
432 				args->final = 1; /* signal we dealt with it */
433 				lws_snprintf(pss->onward, sizeof(pss->onward) - 1,
434 					 "%s%s", vhd->urlroot, args->p);
435 				lwsl_notice("redirecting to ourselves with "
436 					    "cookie refresh\n");
437 				/* we need a redirect to ourselves,
438 				 * session cookie is expired */
439 				goto redirect_with_cookie;
440 			}
441 		} else
442 			lwsl_notice("%s: failed to get sid from wsi\n", __func__);
443 
444 		n = lwsgs_get_auth_level(vhd, username);
445 		lwsl_notice("%s: lwsgs_get_auth_level '%s' says %d\n", __func__, username, n);
446 
447 		if ((args->max_len & n) != args->max_len) {
448 			lwsl_notice("Access rights fail 0x%X vs 0x%X (cookie %s)\n",
449 					args->max_len, n, sid.id);
450 			return 1;
451 		}
452 		lwsl_debug("Access rights OK\n");
453 		break;
454 
455 	case LWS_CALLBACK_SESSION_INFO:
456 	{
457 		struct lwsgs_user u;
458 		sinfo = (struct lws_session_info *)in;
459 		sinfo->username[0] = '\0';
460 		sinfo->email[0] = '\0';
461 		sinfo->ip[0] = '\0';
462 		sinfo->session[0] = '\0';
463 		sinfo->mask = 0;
464 
465 		sid.id[0] = '\0';
466 		lwsl_debug("LWS_CALLBACK_SESSION_INFO\n");
467 		if (lwsgs_get_sid_from_wsi(wsi, &sid))
468 			break;
469 		if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username)))
470 			break;
471 
472 		lws_snprintf(s, sizeof(s) - 1,
473 			 "select username, email from users where username='%s';",
474 			 username);
475 		if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
476 		    SQLITE_OK) {
477 			lwsl_err("Unable to lookup token: %s\n",
478 				 sqlite3_errmsg(vhd->pdb));
479 			break;
480 		}
481 		lws_strncpy(sinfo->username, u.username, sizeof(sinfo->username));
482 		lws_strncpy(sinfo->email, u.email, sizeof(sinfo->email));
483 		lws_strncpy(sinfo->session, sid.id, sizeof(sinfo->session));
484 		sinfo->mask = lwsgs_get_auth_level(vhd, username);
485 		lws_get_peer_simple(wsi, sinfo->ip, sizeof(sinfo->ip));
486 	}
487 
488 		break;
489 
490 	case LWS_CALLBACK_PROCESS_HTML:
491 
492 		args = (struct lws_process_html_args *)in;
493 		{
494 			static const char * const vars[] = {
495 				"$lwsgs_user",
496 				"$lwsgs_auth",
497 				"$lwsgs_email"
498 			};
499 			struct lwsgs_subst_args a;
500 
501 			a.vhd = vhd;
502 			a.pss = pss;
503 			a.wsi = wsi;
504 
505 			pss->phs.vars = vars;
506 			pss->phs.count_vars = LWS_ARRAY_SIZE(vars);
507 			pss->phs.replace = lwsgs_subst;
508 			pss->phs.data = &a;
509 
510 			if (lws_chunked_html_process(args, &pss->phs))
511 				return -1;
512 		}
513 		break;
514 
515 	case LWS_CALLBACK_HTTP_BODY:
516 		if (len < 2) {
517 			lwsl_err("%s: HTTP_BODY: len %d < 2\n", __func__, (int)len);
518 			break;
519 		}
520 
521 		if (!pss->spa) {
522 			pss->spa = lws_spa_create(wsi, param_names,
523 					LWS_ARRAY_SIZE(param_names), 1024,
524 						NULL, NULL);
525 			if (!pss->spa)
526 				return -1;
527 		}
528 
529 		if (lws_spa_process(pss->spa, in, len)) {
530 			lwsl_notice("spa process blew\n");
531 			return -1;
532 		}
533 		break;
534 
535 	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
536 
537 		lwsl_debug("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION\n", __func__);
538 
539 		if (!pss->spa)
540 			break;
541 
542 		cp1 = (const char *)pss->onward;
543 		if (*cp1 == '/')
544 			cp1++;
545 
546 
547 		lws_spa_finalize(pss->spa);
548 		n = strlen(cp1);
549 
550 		if (lws_get_effective_host(wsi, cookie, sizeof(cookie)))
551 			return 1;
552 
553 		if (!strcmp(cp1 + n - 12, "lwsgs-change")) {
554 			if (!lwsgs_handler_change_password(vhd, wsi, pss)) {
555 				cp = lws_spa_get_string(pss->spa, FGS_GOOD);
556 				goto pass;
557 			}
558 
559 			cp = lws_spa_get_string(pss->spa, FGS_BAD);
560 			lwsl_notice("user/password no good %s\n",
561 				lws_spa_get_string(pss->spa, FGS_USERNAME));
562 			lws_snprintf(pss->onward, sizeof(pss->onward),
563 				     "%s%s", vhd->urlroot, cp);
564 
565 			pss->onward[sizeof(pss->onward) - 1] = '\0';
566 			goto completion_flow;
567 		}
568 
569 		if (!strcmp(cp1 + n - 11, "lwsgs-login")) {
570 			lwsl_err("%s: lwsgs-login\n", __func__);
571 			if (lws_spa_get_string(pss->spa, FGS_FORGOT) &&
572 			    lws_spa_get_string(pss->spa, FGS_FORGOT)[0]) {
573 				if (lwsgs_handler_forgot_pw_form(vhd, wsi, pss)) {
574 					n = FGS_FORGOT_BAD;
575 					goto reg_done;
576 				}
577 #if defined(LWS_WITH_SMTP)
578 				/* get the email monitor to take a look */
579 				lws_smtpc_kick(vhd->smtp_client);
580 #endif
581 				n = FGS_FORGOT_GOOD;
582 				goto reg_done;
583 			}
584 
585 			if (!lws_spa_get_string(pss->spa, FGS_USERNAME) ||
586 			    !lws_spa_get_string(pss->spa, FGS_PASSWORD)) {
587 				lwsl_notice("username '%s' or pw '%s' missing\n",
588 					lws_spa_get_string(pss->spa, FGS_USERNAME),
589 					lws_spa_get_string(pss->spa, FGS_PASSWORD));
590 				return -1;
591 			}
592 
593 			if (lws_spa_get_string(pss->spa, FGS_REGISTER) &&
594 			    lws_spa_get_string(pss->spa, FGS_REGISTER)[0]) {
595 
596 				if (lwsgs_handler_register_form(vhd, wsi, pss))
597 					n = FGS_REG_BAD;
598 				else {
599 					n = FGS_REG_GOOD;
600 #if defined(LWS_WITH_SMTP)
601 					/* get the email monitor to take a look */
602 					lws_smtpc_kick(vhd->smtp_client);
603 #endif
604 				}
605 reg_done:
606 				lws_snprintf(pss->onward, sizeof(pss->onward),
607 					     "%s%s", vhd->urlroot,
608 					     lws_spa_get_string(pss->spa, n));
609 
610 				pss->login_expires = 0;
611 				pss->logging_out = 1;
612 				goto completion_flow;
613 			}
614 
615 			/* we have the username and password... check if admin */
616 			if (lwsgw_check_admin(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
617 					      lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
618 				if (lws_spa_get_string(pss->spa, FGS_ADMIN))
619 					cp = lws_spa_get_string(pss->spa, FGS_ADMIN);
620 				else
621 					if (lws_spa_get_string(pss->spa, FGS_GOOD))
622 						cp = lws_spa_get_string(pss->spa, FGS_GOOD);
623 					else {
624 						lwsl_info("No admin or good target url in form\n");
625 						return -1;
626 					}
627 				lwsl_debug("admin\n");
628 				goto pass;
629 			}
630 
631 			/* check users in database */
632 
633 			if (!lwsgs_check_credentials(vhd,
634 					lws_spa_get_string(pss->spa, FGS_USERNAME),
635 					lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
636 				lwsl_notice("pw hash check met\n");
637 				cp = lws_spa_get_string(pss->spa, FGS_GOOD);
638 				goto pass;
639 			} else
640 				lwsl_notice("user/password no good %s %s\n",
641 						lws_spa_get_string(pss->spa, FGS_USERNAME),
642 						lws_spa_get_string(pss->spa, FGS_PASSWORD));
643 
644 			if (!lws_spa_get_string(pss->spa, FGS_BAD)) {
645 				lwsl_info("No admin or good target url in form\n");
646 				return -1;
647 			}
648 
649 			lws_snprintf(pss->onward, sizeof(pss->onward),
650 				     "%s%s", vhd->urlroot,
651 				     lws_spa_get_string(pss->spa, FGS_BAD));
652 
653 			lwsl_notice("failed: %s\n", pss->onward);
654 
655 			goto completion_flow;
656 		}
657 
658 		if (!strcmp(cp1 + n - 12, "lwsgs-logout")) {
659 
660 			lwsl_notice("/logout\n");
661 
662 			if (lwsgs_get_sid_from_wsi(wsi, &pss->login_session)) {
663 				lwsl_notice("not logged in...\n");
664 				return 1;
665 			}
666 
667 			/*
668 			 * We keep the same session, but mark it as not
669 			 * being associated to any authenticated user
670 			 */
671 
672 			lwsgw_update_session(vhd, &pss->login_session, "");
673 
674 			if (!lws_spa_get_string(pss->spa, FGS_GOOD)) {
675 				lwsl_info("No admin or good target url in form\n");
676 				return -1;
677 			}
678 
679 			lws_snprintf(pss->onward, sizeof(pss->onward),
680 				     "%s%s", vhd->urlroot,
681 				     lws_spa_get_string(pss->spa, FGS_GOOD));
682 
683 			pss->login_expires = 0;
684 			pss->logging_out = 1;
685 
686 			goto completion_flow;
687 		}
688 
689 		break;
690 
691 pass:
692 		lws_snprintf(pss->onward, sizeof(pss->onward),
693 			     "%s%s", vhd->urlroot, cp);
694 
695 		if (lwsgs_get_sid_from_wsi(wsi, &sid))
696 			sid.id[0] = '\0';
697 
698 		pss->login_expires = lws_now_secs() +
699 				     vhd->timeout_absolute_secs;
700 
701 		if (!sid.id[0]) {
702 			/* we need to create a new, authorized session */
703 
704 			if (lwsgs_new_session_id(vhd, &pss->login_session,
705 					lws_spa_get_string(pss->spa, FGS_USERNAME),
706 					pss->login_expires))
707 				goto try_to_reuse;
708 
709 			lwsl_notice("%s: Creating new session: %s\n", __func__,
710 				    pss->login_session.id);
711 		} else {
712 			/*
713 			 * we can just update the existing session to be
714 			 * authorized
715 			 */
716 			lwsl_notice("%s: Authorizing existing session %s, name %s\n",
717 				    __func__, sid.id,
718 				    lws_spa_get_string(pss->spa, FGS_USERNAME));
719 			lwsgw_update_session(vhd, &sid,
720 				lws_spa_get_string(pss->spa, FGS_USERNAME));
721 			pss->login_session = sid;
722 		}
723 
724 completion_flow:
725 		lwsgw_expire_old_sessions(vhd);
726 		goto redirect_with_cookie;
727 
728 	case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
729 		if (pss && pss->spa) {
730 			lws_spa_destroy(pss->spa);
731 			pss->spa = NULL;
732 		}
733 		break;
734 
735 	case LWS_CALLBACK_ADD_HEADERS:
736 		lwsgw_expire_old_sessions(vhd);
737 
738 		lwsl_warn("ADD_HEADERS\n");
739 
740 		args = (struct lws_process_html_args *)in;
741 		if (!pss)
742 			return 1;
743 		if (pss->delete_session.id[0]) {
744 			pc = cookie;
745 			lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
746 						  cookie + sizeof(cookie) - 1);
747 
748 			lwsl_notice("deleting cookie '%s'\n", cookie);
749 
750 			if (lws_add_http_header_by_name(wsi,
751 					(unsigned char *)"set-cookie:",
752 					(unsigned char *)cookie, pc - cookie,
753 					(unsigned char **)&args->p,
754 					(unsigned char *)args->p + args->max_len))
755 				return 1;
756 		}
757 
758 		if (!pss->login_session.id[0])
759 			lwsgs_get_sid_from_wsi(wsi, &pss->login_session);
760 
761 		if (!pss->login_session.id[0] && !pss->logging_out) {
762 
763 			pss->login_expires = lws_now_secs() +
764 					     vhd->timeout_anon_absolute_secs;
765 			if (lwsgs_new_session_id(vhd, &pss->login_session, "",
766 						 pss->login_expires))
767 				goto try_to_reuse;
768 			pc = cookie;
769 			lwsgw_cookie_from_session(&pss->login_session,
770 						  pss->login_expires, &pc,
771 						  cookie + sizeof(cookie) - 1);
772 
773 			lwsl_info("LWS_CALLBACK_ADD_HEADERS: setting cookie '%s'\n", cookie);
774 			if (lws_add_http_header_by_name(wsi,
775 					(unsigned char *)"set-cookie:",
776 					(unsigned char *)cookie, pc - cookie,
777 					(unsigned char **)&args->p,
778 					(unsigned char *)args->p + args->max_len))
779 				return 1;
780 		}
781 		break;
782 
783 	default:
784 		break;
785 	}
786 
787 	return 0;
788 
789 redirect_with_cookie:
790 	p = buffer + LWS_PRE;
791 	start = p;
792 	end = p + sizeof(buffer) - LWS_PRE;
793 
794 	lwsl_warn("%s: redirect_with_cookie\n", __func__);
795 
796 	if (lws_add_http_header_status(wsi, HTTP_STATUS_SEE_OTHER, &p, end))
797 		return 1;
798 
799 	{
800 		char loc[1024], uria[128];
801 
802 		uria[0] = '\0';
803 		lws_hdr_copy_fragment(wsi, uria, sizeof(uria),
804 					  WSI_TOKEN_HTTP_URI_ARGS, 0);
805 		n = lws_snprintf(loc, sizeof(loc), "%s?%s",
806 				pss->onward, uria);
807 		lwsl_notice("%s: redirect to '%s'\n", __func__, loc);
808 		if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
809 				(unsigned char *)loc, n, &p, end))
810 			return 1;
811 	}
812 
813 	if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
814 			(unsigned char *)"text/html", 9, &p, end))
815 		return 1;
816 	if (lws_add_http_header_content_length(wsi, 0, &p, end))
817 		return 1;
818 
819 	if (pss->delete_session.id[0]) {
820 		lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
821 					  cookie + sizeof(cookie) - 1);
822 
823 		lwsl_notice("deleting cookie '%s'\n", cookie);
824 
825 		if (lws_add_http_header_by_name(wsi,
826 				(unsigned char *)"set-cookie:",
827 				(unsigned char *)cookie, pc - cookie,
828 				&p, end)) {
829 			lwsl_err("fail0\n");
830 			return 1;
831 		}
832 	}
833 
834 	if (!pss->login_session.id[0]) {
835 		pss->login_expires = lws_now_secs() +
836 				     vhd->timeout_anon_absolute_secs;
837 		if (lwsgs_new_session_id(vhd, &pss->login_session, "",
838 					 pss->login_expires)) {
839 			lwsl_err("fail1\n");
840 			return 1;
841 		}
842 	} else
843 		pss->login_expires = lws_now_secs() +
844 				     vhd->timeout_absolute_secs;
845 
846 	if (pss->login_session.id[0] || pss->logging_out) {
847 		/*
848 		 * we succeeded to login, we must issue a login
849 		 * cookie with the prepared data
850 		 */
851 		pc = cookie;
852 
853 		lwsgw_cookie_from_session(&pss->login_session,
854 					  pss->login_expires, &pc,
855 					  cookie + sizeof(cookie) - 1);
856 
857 		lwsl_err("%s: setting cookie '%s'\n", __func__, cookie);
858 
859 		pss->logging_out = 0;
860 
861 		if (lws_add_http_header_by_name(wsi,
862 				(unsigned char *)"set-cookie:",
863 				(unsigned char *)cookie, pc - cookie,
864 				&p, end)) {
865 			lwsl_err("fail2\n");
866 			return 1;
867 		}
868 	}
869 
870 	if (lws_finalize_http_header(wsi, &p, end))
871 		return 1;
872 
873 	// lwsl_hexdump_notice(start, p - start);
874 
875 	n = lws_write(wsi, start, p - start, LWS_WRITE_H2_STREAM_END |
876 					     LWS_WRITE_HTTP_HEADERS);
877 	if (n < 0)
878 		return 1;
879 
880 	/* fallthru */
881 
882 try_to_reuse:
883 	if (lws_http_transaction_completed(wsi))
884 		return -1;
885 
886 	return 0;
887 }
888 
889 static const struct lws_protocols protocols[] = {
890 	{
891 		"protocol-generic-sessions",
892 		callback_generic_sessions,
893 		sizeof(struct per_session_data__gs),
894 		1024,
895 	},
896 };
897 
898 LWS_VISIBLE int
init_protocol_generic_sessions(struct lws_context * context,struct lws_plugin_capability * c)899 init_protocol_generic_sessions(struct lws_context *context,
900 			struct lws_plugin_capability *c)
901 {
902 	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
903 		lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
904 			 c->api_magic);
905 		return 1;
906 	}
907 
908 	c->protocols = protocols;
909 	c->count_protocols = LWS_ARRAY_SIZE(protocols);
910 	c->extensions = NULL;
911 	c->count_extensions = 0;
912 
913 	return 0;
914 }
915 
916 LWS_VISIBLE int
destroy_protocol_generic_sessions(struct lws_context * context)917 destroy_protocol_generic_sessions(struct lws_context *context)
918 {
919 	return 0;
920 }
921