123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- /*
- * ws protocol handler plugin for "generic sessions"
- *
- * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
- #include "private-lwsgs.h"
- /* handle account confirmation links */
- int
- lwsgs_handler_confirm(struct per_vhost_data__gs *vhd, struct lws *wsi,
- struct per_session_data__gs *pss)
- {
- char cookie[1024], s[256], esc[50];
- struct lws_gs_event_args a;
- struct lwsgs_user u;
- if (lws_hdr_copy_fragment(wsi, cookie, sizeof(cookie),
- WSI_TOKEN_HTTP_URI_ARGS, 0) < 0)
- goto verf_fail;
- if (strncmp(cookie, "token=", 6))
- goto verf_fail;
- u.username[0] = '\0';
- lws_snprintf(s, sizeof(s) - 1,
- "select username,email,verified from users where token = '%s';",
- lws_sql_purify(esc, &cookie[6], sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- goto verf_fail;
- }
- if (!u.username[0] || u.verified != 1) {
- lwsl_notice("verify token doesn't map to unverified user\n");
- goto verf_fail;
- }
- lwsl_notice("Verifying %s\n", u.username);
- lws_snprintf(s, sizeof(s) - 1,
- "update users set verified=%d where username='%s';",
- LWSGS_VERIFIED_ACCEPTED,
- lws_sql_purify(esc, u.username, sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- goto verf_fail;
- }
- lwsl_notice("deleting account\n");
- a.event = LWSGSE_CREATED;
- a.username = u.username;
- a.email = u.email;
- lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0);
- lws_snprintf(pss->onward, sizeof(pss->onward),
- "%s/post-verify-ok.html", vhd->email_confirm_url);
- pss->login_expires = lws_now_secs() + vhd->timeout_absolute_secs;
- pss->delete_session.id[0] = '\0';
- lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
- /* we need to create a new, authorized session */
- if (lwsgs_new_session_id(vhd, &pss->login_session, u.username,
- pss->login_expires))
- goto verf_fail;
- lwsl_notice("Creating new session: %s, redir to %s\n",
- pss->login_session.id, pss->onward);
- return 0;
- verf_fail:
- pss->delete_session.id[0] = '\0';
- lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
- pss->login_expires = 0;
- lws_snprintf(pss->onward, sizeof(pss->onward), "%s/post-verify-fail.html",
- vhd->email_confirm_url);
- return 1;
- }
- /* handle forgot password confirmation links */
- int
- lwsgs_handler_forgot(struct per_vhost_data__gs *vhd, struct lws *wsi,
- struct per_session_data__gs *pss)
- {
- char cookie[1024], s[256], esc[50];
- struct lwsgs_user u;
- const char *a;
- a = lws_get_urlarg_by_name(wsi, "token=", cookie, sizeof(cookie));
- if (!a)
- goto forgot_fail;
- u.username[0] = '\0';
- lws_snprintf(s, sizeof(s) - 1,
- "select username,verified from users where verified=%d and "
- "token = '%s' and token_time != 0;",
- LWSGS_VERIFIED_ACCEPTED,
- lws_sql_purify(esc, &cookie[6], sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- goto forgot_fail;
- }
- if (!u.username[0]) {
- puts(s);
- lwsl_notice("forgot token doesn't map to verified user\n");
- goto forgot_fail;
- }
- /* mark user as having validated forgot flow just now */
- lws_snprintf(s, sizeof(s) - 1,
- "update users set token_time=0,last_forgot_validated=%lu "
- "where username='%s';",
- (unsigned long)lws_now_secs(),
- lws_sql_purify(esc, u.username, sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- goto forgot_fail;
- }
- a = lws_get_urlarg_by_name(wsi, "good=", cookie, sizeof(cookie));
- if (!a)
- a = "broken-forget-post-good-url";
- lws_snprintf(pss->onward, sizeof(pss->onward),
- "%s/%s", vhd->email_confirm_url, a);
- pss->login_expires = lws_now_secs() + vhd->timeout_absolute_secs;
- pss->delete_session.id[0] = '\0';
- lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
- /* we need to create a new, authorized session */
- if (lwsgs_new_session_id(vhd, &pss->login_session,
- u.username,
- pss->login_expires))
- goto forgot_fail;
- lwsl_notice("Creating new session: %s, redir to %s\n",
- pss->login_session.id, pss->onward);
- return 0;
- forgot_fail:
- pss->delete_session.id[0] = '\0';
- lwsgs_get_sid_from_wsi(wsi, &pss->delete_session);
- pss->login_expires = 0;
- a = lws_get_urlarg_by_name(wsi, "bad=", cookie, sizeof(cookie));
- if (!a)
- a = "broken-forget-post-bad-url";
- lws_snprintf(pss->onward, sizeof(pss->onward), "%s/%s",
- vhd->email_confirm_url, a);
- return 1;
- }
- /* support dynamic username / email checking */
- int
- lwsgs_handler_check(struct per_vhost_data__gs *vhd,
- struct lws *wsi, struct per_session_data__gs *pss)
- {
- static const char * const colname[] = { "username", "email" };
- char cookie[1024], s[256], esc[50], *pc;
- unsigned char *p, *start, *end, buffer[LWS_PRE + 256];
- struct lwsgs_user u;
- int n;
- /*
- * either /check?email=xxx@yyy or: /check?username=xxx
- * returns '0' if not already registered, else '1'
- */
- u.username[0] = '\0';
- if (lws_hdr_copy_fragment(wsi, cookie, sizeof(cookie),
- WSI_TOKEN_HTTP_URI_ARGS, 0) < 0)
- goto reply;
- n = !strncmp(cookie, "email=", 6);
- pc = strchr(cookie, '=');
- if (!pc) {
- lwsl_notice("cookie has no =\n");
- goto reply;
- }
- pc++;
- /* admin user cannot be registered in user db */
- if (!strcmp(vhd->admin_user, pc)) {
- u.username[0] = 'a';
- goto reply;
- }
- lws_snprintf(s, sizeof(s) - 1,
- "select username, email from users where %s = '%s';",
- colname[n], lws_sql_purify(esc, pc, sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- goto reply;
- }
- reply:
- s[0] = '0' + !!u.username[0];
- p = buffer + LWS_PRE;
- start = p;
- end = p + sizeof(buffer) - LWS_PRE;
- if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
- return -1;
- if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
- (unsigned char *)"text/plain", 10,
- &p, end))
- return -1;
- if (lws_add_http_header_content_length(wsi, 1, &p, end))
- return -1;
- if (lws_finalize_http_header(wsi, &p, end))
- return -1;
- n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
- if (n != (p - start)) {
- lwsl_err("_write returned %d from %ld\n", n, (long)(p - start));
- return -1;
- }
- n = lws_write(wsi, (unsigned char *)s, 1, LWS_WRITE_HTTP);
- if (n != 1)
- return -1;
- return 0;
- }
- /* handle forgot password confirmation links */
- int
- lwsgs_handler_change_password(struct per_vhost_data__gs *vhd, struct lws *wsi,
- struct per_session_data__gs *pss)
- {
- char s[256], esc[50], username[50];
- struct lwsgs_user u;
- lwsgw_hash sid;
- int n = 0;
- /* see if he's logged in */
- username[0] = '\0';
- if (!lwsgs_get_sid_from_wsi(wsi, &sid)) {
- u.username[0] = '\0';
- if (!lwsgs_lookup_session(vhd, &sid, username, sizeof(username))) {
- n = 1; /* yes, logged in */
- if (lwsgs_lookup_user(vhd, username, &u))
- return 1;
- /* did a forgot pw ? */
- if (u.last_forgot_validated > lws_now_secs() - 300) {
- n |= LWSGS_AUTH_FORGOT_FLOW;
- lwsl_debug("within forgot password flow\n");
- }
- }
- }
- lwsl_debug("auth value %d\n", n);
- /* if he just did forgot pw flow, don't need old pw */
- if ((n & (LWSGS_AUTH_FORGOT_FLOW | 1)) != (LWSGS_AUTH_FORGOT_FLOW | 1)) {
- /* otherwise user:pass must be right */
- lwsl_debug("checking pw\n");
- if (lwsgs_check_credentials(vhd,
- lws_spa_get_string(pss->spa, FGS_USERNAME),
- lws_spa_get_string(pss->spa, FGS_CURPW))) {
- lwsl_notice("credentials bad\n");
- return 1;
- }
- lwsl_debug("current pw checks out\n");
- strncpy(u.username, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(u.username) - 1);
- u.username[sizeof(u.username) - 1] = '\0';
- }
- /* does he want to delete his account? */
- if (lws_spa_get_length(pss->spa, FGS_DELETE)) {
- struct lws_gs_event_args a;
- lwsl_notice("deleting account\n");
- a.event = LWSGSE_DELETED;
- a.username = u.username;
- a.email = "";
- lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0);
- lws_snprintf(s, sizeof(s) - 1,
- "delete from users where username='%s';"
- "delete from sessions where username='%s';",
- lws_sql_purify(esc, u.username, sizeof(esc) - 1),
- lws_sql_purify(esc, u.username, sizeof(esc) - 1));
- goto sql;
- }
- if (lwsgs_hash_password(vhd, lws_spa_get_string(pss->spa, FGS_PASSWORD), &u))
- return 1;
- lwsl_notice("updating password hash\n");
- lws_snprintf(s, sizeof(s) - 1,
- "update users set pwhash='%s', pwsalt='%s', "
- "last_forgot_validated=0 where username='%s';",
- u.pwhash.id, u.pwsalt.id,
- lws_sql_purify(esc, u.username, sizeof(esc) - 1));
- sql:
- if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
- lwsl_err("Unable to update pw hash: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- return 0;
- }
- int
- lwsgs_handler_forgot_pw_form(struct per_vhost_data__gs *vhd,
- struct lws *wsi,
- struct per_session_data__gs *pss)
- {
- char s[LWSGS_EMAIL_CONTENT_SIZE];
- unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
- char esc[50], esc1[50], esc2[50], esc3[50], esc4[50];
- struct lwsgs_user u;
- lwsgw_hash hash;
- unsigned char sid_rand[20];
- int n;
- lwsl_notice("FORGOT %s %s\n",
- lws_spa_get_string(pss->spa, FGS_USERNAME),
- lws_spa_get_string(pss->spa, FGS_EMAIL));
- if (!lws_spa_get_string(pss->spa, FGS_USERNAME) &&
- !lws_spa_get_string(pss->spa, FGS_EMAIL)) {
- lwsl_err("Form must provide either "
- "username or email\n");
- return -1;
- }
- if (!lws_spa_get_string(pss->spa, FGS_FORGOT_GOOD) ||
- !lws_spa_get_string(pss->spa, FGS_FORGOT_BAD) ||
- !lws_spa_get_string(pss->spa, FGS_FORGOT_POST_GOOD) ||
- !lws_spa_get_string(pss->spa, FGS_FORGOT_POST_BAD)) {
- lwsl_err("Form must provide reg-good "
- "and reg-bad (and post-*)"
- "targets\n");
- return -1;
- }
- u.username[0] = '\0';
- if (lws_spa_get_string(pss->spa, FGS_USERNAME))
- lws_snprintf(s, sizeof(s) - 1,
- "select username,email "
- "from users where username = '%s';",
- lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME),
- sizeof(esc) - 1));
- else
- lws_snprintf(s, sizeof(s) - 1,
- "select username,email "
- "from users where email = '%s';",
- lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- if (!u.username[0]) {
- lwsl_err("No match found %s\n", s);
- return 1;
- }
- lws_get_peer_simple(wsi, pss->ip, sizeof(pss->ip));
- if (lws_get_random(vhd->context, sid_rand,
- sizeof(sid_rand)) !=
- sizeof(sid_rand)) {
- lwsl_err("Problem getting random for token\n");
- return 1;
- }
- sha1_to_lwsgw_hash(sid_rand, &hash);
- n = lws_snprintf(s, sizeof(s),
- "From: Forgot Password Assistant Noreply <%s>\n"
- "To: %s <%s>\n"
- "Subject: Password reset request\n"
- "\n"
- "Hello, %s\n\n"
- "We received a password reset request from IP %s for this email,\n"
- "to confirm you want to do that, please click the link below.\n\n",
- lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1),
- lws_sql_purify(esc1, u.username, sizeof(esc1) - 1),
- lws_sql_purify(esc2, u.email, sizeof(esc2) - 1),
- lws_sql_purify(esc3, u.username, sizeof(esc3) - 1),
- lws_sql_purify(esc4, pss->ip, sizeof(esc4) - 1));
- lws_snprintf(s + n, sizeof(s) -n,
- "%s/lwsgs-forgot?token=%s"
- "&good=%s"
- "&bad=%s\n\n"
- "If this request is unexpected, please ignore it and\n"
- "no further action will be taken.\n\n"
- "If you have any questions or concerns about this\n"
- "automated email, you can contact a real person at\n"
- "%s.\n"
- "\n.\n",
- vhd->email_confirm_url, hash.id,
- lws_urlencode(esc1,
- lws_spa_get_string(pss->spa, FGS_FORGOT_POST_GOOD),
- sizeof(esc1) - 1),
- lws_urlencode(esc3,
- lws_spa_get_string(pss->spa, FGS_FORGOT_POST_BAD),
- sizeof(esc3) - 1),
- vhd->email_contact_person);
- lws_snprintf((char *)buffer, sizeof(buffer) - 1,
- "insert into email(username, content)"
- " values ('%s', '%s');",
- lws_sql_purify(esc, u.username, sizeof(esc) - 1), s);
- if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL,
- NULL, NULL) != SQLITE_OK) {
- lwsl_err("Unable to insert email: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- lws_snprintf(s, sizeof(s) - 1,
- "update users set token='%s',token_time='%ld' where username='%s';",
- hash.id, (long)lws_now_secs(),
- lws_sql_purify(esc, u.username, sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) !=
- SQLITE_OK) {
- lwsl_err("Unable to set token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- return 0;
- }
- int
- lwsgs_handler_register_form(struct per_vhost_data__gs *vhd,
- struct lws *wsi,
- struct per_session_data__gs *pss)
- {
- unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
- char esc[50], esc1[50], esc2[50], esc3[50], esc4[50];
- char s[LWSGS_EMAIL_CONTENT_SIZE];
- unsigned char sid_rand[20];
- struct lwsgs_user u;
- lwsgw_hash hash;
- lwsl_notice("REGISTER %s %s %s\n",
- lws_spa_get_string(pss->spa, FGS_USERNAME),
- lws_spa_get_string(pss->spa, FGS_PASSWORD),
- lws_spa_get_string(pss->spa, FGS_EMAIL));
- if (lwsgs_get_sid_from_wsi(wsi,
- &pss->login_session))
- return 1;
- lws_get_peer_simple(wsi, pss->ip, sizeof(pss->ip));
- lwsl_notice("IP=%s\n", pss->ip);
- if (!lws_spa_get_string(pss->spa, FGS_REG_GOOD) ||
- !lws_spa_get_string(pss->spa, FGS_REG_BAD)) {
- lwsl_info("Form must provide reg-good and reg-bad targets\n");
- return -1;
- }
- /* admin user cannot be registered in user db */
- if (!strcmp(vhd->admin_user,
- lws_spa_get_string(pss->spa, FGS_USERNAME)))
- return 1;
- if (!lwsgs_lookup_user(vhd,
- lws_spa_get_string(pss->spa, FGS_USERNAME), &u)) {
- lwsl_notice("user %s already registered\n",
- lws_spa_get_string(pss->spa, FGS_USERNAME));
- return 1;
- }
- u.username[0] = '\0';
- lws_snprintf(s, sizeof(s) - 1, "select username, email from users where email = '%s';",
- lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_EMAIL),
- sizeof(esc) - 1));
- if (sqlite3_exec(vhd->pdb, s,
- lwsgs_lookup_callback_user, &u, NULL) != SQLITE_OK) {
- lwsl_err("Unable to lookup token: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- if (u.username[0]) {
- lwsl_notice("email %s already in use\n",
- lws_spa_get_string(pss->spa, FGS_USERNAME));
- return 1;
- }
- if (lwsgs_hash_password(vhd, lws_spa_get_string(pss->spa, FGS_PASSWORD),
- &u)) {
- lwsl_err("Password hash failed\n");
- return 1;
- }
- if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
- sizeof(sid_rand)) {
- lwsl_err("Problem getting random for token\n");
- return 1;
- }
- sha1_to_lwsgw_hash(sid_rand, &hash);
- lws_snprintf((char *)buffer, sizeof(buffer) - 1,
- "insert into users(username,"
- " creation_time, ip, email, verified,"
- " pwhash, pwsalt, token, last_forgot_validated)"
- " values ('%s', %lu, '%s', '%s', 0,"
- " '%s', '%s', '%s', 0);",
- lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc) - 1),
- (unsigned long)lws_now_secs(),
- lws_sql_purify(esc1, pss->ip, sizeof(esc1) - 1),
- lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1),
- u.pwhash.id, u.pwsalt.id, hash.id);
- if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, NULL, NULL) != SQLITE_OK) {
- lwsl_err("Unable to insert user: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- lws_snprintf(s, sizeof(s),
- "From: Noreply <%s>\n"
- "To: %s <%s>\n"
- "Subject: Registration verification\n"
- "\n"
- "Hello, %s\n\n"
- "We received a registration from IP %s using this email,\n"
- "to confirm it is legitimate, please click the link below.\n\n"
- "%s/lwsgs-confirm?token=%s\n\n"
- "If this request is unexpected, please ignore it and\n"
- "no further action will be taken.\n\n"
- "If you have any questions or concerns about this\n"
- "automated email, you can contact a real person at\n"
- "%s.\n"
- "\n.\n",
- lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1),
- lws_sql_purify(esc1, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc1) - 1),
- lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1),
- lws_sql_purify(esc3, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc3) - 1),
- lws_sql_purify(esc4, pss->ip, sizeof(esc4) - 1),
- vhd->email_confirm_url, hash.id,
- vhd->email_contact_person);
- lws_snprintf((char *)buffer, sizeof(buffer) - 1,
- "insert into email(username, content) values ('%s', '%s');",
- lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME),
- sizeof(esc) - 1), s);
- if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, NULL, NULL) != SQLITE_OK) {
- lwsl_err("Unable to insert email: %s\n",
- sqlite3_errmsg(vhd->pdb));
- return 1;
- }
- return 0;
- }
|