2 * ws protocol handler plugin for "generic sessions"
4 * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 #include "private-lwsgs.h"
24 /* handle account confirmation links */
27 lwsgs_handler_confirm(struct per_vhost_data__gs *vhd, struct lws *wsi,
28 struct per_session_data__gs *pss)
30 char cookie[1024], s[256], esc[50];
31 struct lws_gs_event_args a;
34 if (lws_hdr_copy_fragment(wsi, cookie, sizeof(cookie),
35 WSI_TOKEN_HTTP_URI_ARGS, 0) < 0)
38 if (strncmp(cookie, "token=", 6))
42 lws_snprintf(s, sizeof(s) - 1,
43 "select username,email,verified from users where token = '%s';",
44 lws_sql_purify(esc, &cookie[6], sizeof(esc) - 1));
45 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
47 lwsl_err("Unable to lookup token: %s\n",
48 sqlite3_errmsg(vhd->pdb));
52 if (!u.username[0] || u.verified != 1) {
53 lwsl_notice("verify token doesn't map to unverified user\n");
57 lwsl_notice("Verifying %s\n", u.username);
58 lws_snprintf(s, sizeof(s) - 1,
59 "update users set verified=%d where username='%s';",
60 LWSGS_VERIFIED_ACCEPTED,
61 lws_sql_purify(esc, u.username, sizeof(esc) - 1));
62 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
64 lwsl_err("Unable to lookup token: %s\n",
65 sqlite3_errmsg(vhd->pdb));
70 lwsl_notice("deleting account\n");
72 a.event = LWSGSE_CREATED;
73 a.username = u.username;
75 lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0);
77 lws_snprintf(pss->onward, sizeof(pss->onward),
78 "%s/post-verify-ok.html", vhd->email_confirm_url);
80 pss->login_expires = lws_now_secs() + vhd->timeout_absolute_secs;
82 pss->delete_session.id[0] = '\0';
83 lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
85 /* we need to create a new, authorized session */
87 if (lwsgs_new_session_id(vhd, &pss->login_session, u.username,
91 lwsl_notice("Creating new session: %s, redir to %s\n",
92 pss->login_session.id, pss->onward);
97 pss->delete_session.id[0] = '\0';
98 lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
99 pss->login_expires = 0;
101 lws_snprintf(pss->onward, sizeof(pss->onward), "%s/post-verify-fail.html",
102 vhd->email_confirm_url);
107 /* handle forgot password confirmation links */
110 lwsgs_handler_forgot(struct per_vhost_data__gs *vhd, struct lws *wsi,
111 struct per_session_data__gs *pss)
113 char cookie[1024], s[256], esc[50];
117 a = lws_get_urlarg_by_name(wsi, "token=", cookie, sizeof(cookie));
121 u.username[0] = '\0';
122 lws_snprintf(s, sizeof(s) - 1,
123 "select username,verified from users where verified=%d and "
124 "token = '%s' and token_time != 0;",
125 LWSGS_VERIFIED_ACCEPTED,
126 lws_sql_purify(esc, &cookie[6], sizeof(esc) - 1));
127 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
129 lwsl_err("Unable to lookup token: %s\n",
130 sqlite3_errmsg(vhd->pdb));
135 if (!u.username[0]) {
137 lwsl_notice("forgot token doesn't map to verified user\n");
141 /* mark user as having validated forgot flow just now */
143 lws_snprintf(s, sizeof(s) - 1,
144 "update users set token_time=0,last_forgot_validated=%lu "
145 "where username='%s';",
146 (unsigned long)lws_now_secs(),
147 lws_sql_purify(esc, u.username, sizeof(esc) - 1));
149 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
151 lwsl_err("Unable to lookup token: %s\n",
152 sqlite3_errmsg(vhd->pdb));
156 a = lws_get_urlarg_by_name(wsi, "good=", cookie, sizeof(cookie));
158 a = "broken-forget-post-good-url";
160 lws_snprintf(pss->onward, sizeof(pss->onward),
161 "%s/%s", vhd->email_confirm_url, a);
163 pss->login_expires = lws_now_secs() + vhd->timeout_absolute_secs;
165 pss->delete_session.id[0] = '\0';
166 lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
168 /* we need to create a new, authorized session */
169 if (lwsgs_new_session_id(vhd, &pss->login_session,
174 lwsl_notice("Creating new session: %s, redir to %s\n",
175 pss->login_session.id, pss->onward);
180 pss->delete_session.id[0] = '\0';
181 lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
182 pss->login_expires = 0;
184 a = lws_get_urlarg_by_name(wsi, "bad=", cookie, sizeof(cookie));
186 a = "broken-forget-post-bad-url";
188 lws_snprintf(pss->onward, sizeof(pss->onward), "%s/%s",
189 vhd->email_confirm_url, a);
194 /* support dynamic username / email checking */
197 lwsgs_handler_check(struct per_vhost_data__gs *vhd,
198 struct lws *wsi, struct per_session_data__gs *pss)
200 static const char * const colname[] = { "username", "email" };
201 char cookie[1024], s[256], esc[50], *pc;
202 unsigned char *p, *start, *end, buffer[LWS_PRE + 256];
207 * either /check?email=xxx@yyy or: /check?username=xxx
208 * returns '0' if not already registered, else '1'
211 u.username[0] = '\0';
212 if (lws_hdr_copy_fragment(wsi, cookie, sizeof(cookie),
213 WSI_TOKEN_HTTP_URI_ARGS, 0) < 0)
216 n = !strncmp(cookie, "email=", 6);
217 pc = strchr(cookie, '=');
219 lwsl_notice("cookie has no =\n");
224 /* admin user cannot be registered in user db */
225 if (!strcmp(vhd->admin_user, pc)) {
230 lws_snprintf(s, sizeof(s) - 1,
231 "select username, email from users where %s = '%s';",
232 colname[n], lws_sql_purify(esc, pc, sizeof(esc) - 1));
233 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
235 lwsl_err("Unable to lookup token: %s\n",
236 sqlite3_errmsg(vhd->pdb));
241 s[0] = '0' + !!u.username[0];
242 p = buffer + LWS_PRE;
244 end = p + sizeof(buffer) - LWS_PRE;
246 if (lws_add_http_header_status(wsi, 200, &p, end))
248 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
249 (unsigned char *)"text/plain", 10,
253 if (lws_add_http_header_content_length(wsi, 1, &p, end))
256 if (lws_finalize_http_header(wsi, &p, end))
259 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
260 if (n != (p - start)) {
261 lwsl_err("_write returned %d from %ld\n", n, (long)(p - start));
264 n = lws_write(wsi, (unsigned char *)s, 1, LWS_WRITE_HTTP);
271 /* handle forgot password confirmation links */
274 lwsgs_handler_change_password(struct per_vhost_data__gs *vhd, struct lws *wsi,
275 struct per_session_data__gs *pss)
277 char s[256], esc[50], username[50];
282 /* see if he's logged in */
284 if (!lwsgs_get_sid_from_wsi(wsi, &sid)) {
285 u.username[0] = '\0';
286 if (!lwsgs_lookup_session(vhd, &sid, username, sizeof(username))) {
287 n = 1; /* yes, logged in */
288 if (lwsgs_lookup_user(vhd, username, &u))
291 /* did a forgot pw ? */
292 if (u.last_forgot_validated > lws_now_secs() - 300) {
293 n |= LWSGS_AUTH_FORGOT_FLOW;
294 lwsl_debug("within forgot password flow\n");
299 lwsl_debug("auth value %d\n", n);
301 /* if he just did forgot pw flow, don't need old pw */
302 if ((n & (LWSGS_AUTH_FORGOT_FLOW | 1)) != (LWSGS_AUTH_FORGOT_FLOW | 1)) {
303 /* otherwise user:pass must be right */
304 lwsl_debug("checking pw\n");
305 if (lwsgs_check_credentials(vhd,
306 lws_spa_get_string(pss->spa, FGS_USERNAME),
307 lws_spa_get_string(pss->spa, FGS_CURPW))) {
308 lwsl_notice("credentials bad\n");
312 lwsl_debug("current pw checks out\n");
314 strncpy(u.username, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(u.username) - 1);
315 u.username[sizeof(u.username) - 1] = '\0';
318 /* does he want to delete his account? */
320 if (lws_spa_get_length(pss->spa, FGS_DELETE)) {
321 struct lws_gs_event_args a;
323 lwsl_notice("deleting account\n");
325 a.event = LWSGSE_DELETED;
326 a.username = u.username;
328 lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0);
330 lws_snprintf(s, sizeof(s) - 1,
331 "delete from users where username='%s';"
332 "delete from sessions where username='%s';",
333 lws_sql_purify(esc, u.username, sizeof(esc) - 1),
334 lws_sql_purify(esc, u.username, sizeof(esc) - 1));
338 if (lwsgs_hash_password(vhd, lws_spa_get_string(pss->spa, FGS_PASSWORD), &u))
341 lwsl_notice("updating password hash\n");
343 lws_snprintf(s, sizeof(s) - 1,
344 "update users set pwhash='%s', pwsalt='%s', "
345 "last_forgot_validated=0 where username='%s';",
346 u.pwhash.id, u.pwsalt.id,
347 lws_sql_purify(esc, u.username, sizeof(esc) - 1));
350 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
351 lwsl_err("Unable to update pw hash: %s\n",
352 sqlite3_errmsg(vhd->pdb));
360 lwsgs_handler_forgot_pw_form(struct per_vhost_data__gs *vhd,
362 struct per_session_data__gs *pss)
364 char s[LWSGS_EMAIL_CONTENT_SIZE];
365 unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
366 char esc[50], esc1[50], esc2[50], esc3[50], esc4[50];
369 unsigned char sid_rand[20];
372 lwsl_notice("FORGOT %s %s\n",
373 lws_spa_get_string(pss->spa, FGS_USERNAME),
374 lws_spa_get_string(pss->spa, FGS_EMAIL));
376 if (!lws_spa_get_string(pss->spa, FGS_USERNAME) &&
377 !lws_spa_get_string(pss->spa, FGS_EMAIL)) {
378 lwsl_err("Form must provide either "
379 "username or email\n");
383 if (!lws_spa_get_string(pss->spa, FGS_FORGOT_GOOD) ||
384 !lws_spa_get_string(pss->spa, FGS_FORGOT_BAD) ||
385 !lws_spa_get_string(pss->spa, FGS_FORGOT_POST_GOOD) ||
386 !lws_spa_get_string(pss->spa, FGS_FORGOT_POST_BAD)) {
387 lwsl_err("Form must provide reg-good "
388 "and reg-bad (and post-*)"
393 u.username[0] = '\0';
394 if (lws_spa_get_string(pss->spa, FGS_USERNAME))
395 lws_snprintf(s, sizeof(s) - 1,
396 "select username,email "
397 "from users where username = '%s';",
398 lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME),
401 lws_snprintf(s, sizeof(s) - 1,
402 "select username,email "
403 "from users where email = '%s';",
404 lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc) - 1));
405 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
407 lwsl_err("Unable to lookup token: %s\n",
408 sqlite3_errmsg(vhd->pdb));
411 if (!u.username[0]) {
412 lwsl_err("No match found %s\n", s);
416 lws_get_peer_simple(wsi, pss->ip, sizeof(pss->ip));
417 if (lws_get_random(vhd->context, sid_rand,
420 lwsl_err("Problem getting random for token\n");
423 sha1_to_lwsgw_hash(sid_rand, &hash);
424 n = lws_snprintf(s, sizeof(s),
425 "From: Forgot Password Assistant Noreply <%s>\n"
427 "Subject: Password reset request\n"
430 "We received a password reset request from IP %s for this email,\n"
431 "to confirm you want to do that, please click the link below.\n\n",
432 lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1),
433 lws_sql_purify(esc1, u.username, sizeof(esc1) - 1),
434 lws_sql_purify(esc2, u.email, sizeof(esc2) - 1),
435 lws_sql_purify(esc3, u.username, sizeof(esc3) - 1),
436 lws_sql_purify(esc4, pss->ip, sizeof(esc4) - 1));
437 lws_snprintf(s + n, sizeof(s) -n,
438 "%s/lwsgs-forgot?token=%s"
441 "If this request is unexpected, please ignore it and\n"
442 "no further action will be taken.\n\n"
443 "If you have any questions or concerns about this\n"
444 "automated email, you can contact a real person at\n"
447 vhd->email_confirm_url, hash.id,
449 lws_spa_get_string(pss->spa, FGS_FORGOT_POST_GOOD),
452 lws_spa_get_string(pss->spa, FGS_FORGOT_POST_BAD),
454 vhd->email_contact_person);
456 lws_snprintf((char *)buffer, sizeof(buffer) - 1,
457 "insert into email(username, content)"
458 " values ('%s', '%s');",
459 lws_sql_purify(esc, u.username, sizeof(esc) - 1), s);
460 if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL,
461 NULL, NULL) != SQLITE_OK) {
462 lwsl_err("Unable to insert email: %s\n",
463 sqlite3_errmsg(vhd->pdb));
467 lws_snprintf(s, sizeof(s) - 1,
468 "update users set token='%s',token_time='%ld' where username='%s';",
469 hash.id, (long)lws_now_secs(),
470 lws_sql_purify(esc, u.username, sizeof(esc) - 1));
471 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) !=
473 lwsl_err("Unable to set token: %s\n",
474 sqlite3_errmsg(vhd->pdb));
482 lwsgs_handler_register_form(struct per_vhost_data__gs *vhd,
484 struct per_session_data__gs *pss)
486 unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
487 char esc[50], esc1[50], esc2[50], esc3[50], esc4[50];
488 char s[LWSGS_EMAIL_CONTENT_SIZE];
489 unsigned char sid_rand[20];
493 lwsl_notice("REGISTER %s %s %s\n",
494 lws_spa_get_string(pss->spa, FGS_USERNAME),
495 lws_spa_get_string(pss->spa, FGS_PASSWORD),
496 lws_spa_get_string(pss->spa, FGS_EMAIL));
497 if (lwsgs_get_sid_from_wsi(wsi,
498 &pss->login_session))
501 lws_get_peer_simple(wsi, pss->ip, sizeof(pss->ip));
502 lwsl_notice("IP=%s\n", pss->ip);
504 if (!lws_spa_get_string(pss->spa, FGS_REG_GOOD) ||
505 !lws_spa_get_string(pss->spa, FGS_REG_BAD)) {
506 lwsl_info("Form must provide reg-good and reg-bad targets\n");
510 /* admin user cannot be registered in user db */
511 if (!strcmp(vhd->admin_user,
512 lws_spa_get_string(pss->spa, FGS_USERNAME)))
515 if (!lwsgs_lookup_user(vhd,
516 lws_spa_get_string(pss->spa, FGS_USERNAME), &u)) {
517 lwsl_notice("user %s already registered\n",
518 lws_spa_get_string(pss->spa, FGS_USERNAME));
522 u.username[0] = '\0';
523 lws_snprintf(s, sizeof(s) - 1, "select username, email from users where email = '%s';",
524 lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_EMAIL),
527 if (sqlite3_exec(vhd->pdb, s,
528 lwsgs_lookup_callback_user, &u, NULL) != SQLITE_OK) {
529 lwsl_err("Unable to lookup token: %s\n",
530 sqlite3_errmsg(vhd->pdb));
535 lwsl_notice("email %s already in use\n",
536 lws_spa_get_string(pss->spa, FGS_USERNAME));
540 if (lwsgs_hash_password(vhd, lws_spa_get_string(pss->spa, FGS_PASSWORD),
542 lwsl_err("Password hash failed\n");
546 if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
548 lwsl_err("Problem getting random for token\n");
551 sha1_to_lwsgw_hash(sid_rand, &hash);
553 lws_snprintf((char *)buffer, sizeof(buffer) - 1,
554 "insert into users(username,"
555 " creation_time, ip, email, verified,"
556 " pwhash, pwsalt, token, last_forgot_validated)"
557 " values ('%s', %lu, '%s', '%s', 0,"
558 " '%s', '%s', '%s', 0);",
559 lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc) - 1),
560 (unsigned long)lws_now_secs(),
561 lws_sql_purify(esc1, pss->ip, sizeof(esc1) - 1),
562 lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1),
563 u.pwhash.id, u.pwsalt.id, hash.id);
565 if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, NULL, NULL) != SQLITE_OK) {
566 lwsl_err("Unable to insert user: %s\n",
567 sqlite3_errmsg(vhd->pdb));
571 lws_snprintf(s, sizeof(s),
572 "From: Noreply <%s>\n"
574 "Subject: Registration verification\n"
577 "We received a registration from IP %s using this email,\n"
578 "to confirm it is legitimate, please click the link below.\n\n"
579 "%s/lwsgs-confirm?token=%s\n\n"
580 "If this request is unexpected, please ignore it and\n"
581 "no further action will be taken.\n\n"
582 "If you have any questions or concerns about this\n"
583 "automated email, you can contact a real person at\n"
586 lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1),
587 lws_sql_purify(esc1, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc1) - 1),
588 lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1),
589 lws_sql_purify(esc3, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc3) - 1),
590 lws_sql_purify(esc4, pss->ip, sizeof(esc4) - 1),
591 vhd->email_confirm_url, hash.id,
592 vhd->email_contact_person);
594 lws_snprintf((char *)buffer, sizeof(buffer) - 1,
595 "insert into email(username, content) values ('%s', '%s');",
596 lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME),
597 sizeof(esc) - 1), s);
599 if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, NULL, NULL) != SQLITE_OK) {
600 lwsl_err("Unable to insert email: %s\n",
601 sqlite3_errmsg(vhd->pdb));