protocol_generic_sessions.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. /*
  2. * ws protocol handler plugin for "generic sessions"
  3. *
  4. * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation:
  9. * version 2.1 of the License.
  10. *
  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.
  15. *
  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,
  19. * MA 02110-1301 USA
  20. */
  21. #include "private-lwsgs.h"
  22. /* keep changes in sync with the enum in lwsgs.h */
  23. static const char * const param_names[] = {
  24. "username",
  25. "password",
  26. "password2",
  27. "email",
  28. "register",
  29. "good",
  30. "bad",
  31. "reg-good",
  32. "reg-bad",
  33. "admin",
  34. "forgot",
  35. "forgot-good",
  36. "forgot-bad",
  37. "forgot-post-good",
  38. "forgot-post-bad",
  39. "change",
  40. "curpw",
  41. "delete"
  42. };
  43. struct lwsgs_fill_args {
  44. char *buf;
  45. int len;
  46. };
  47. static const struct lws_protocols protocols[];
  48. static int
  49. lwsgs_lookup_callback_email(void *priv, int cols, char **col_val,
  50. char **col_name)
  51. {
  52. struct lwsgs_fill_args *a = (struct lwsgs_fill_args *)priv;
  53. int n;
  54. for (n = 0; n < cols; n++) {
  55. if (!strcmp(col_name[n], "content")) {
  56. strncpy(a->buf, col_val[n], a->len - 1);
  57. a->buf[a->len - 1] = '\0';
  58. continue;
  59. }
  60. }
  61. return 0;
  62. }
  63. static int
  64. lwsgs_email_cb_get_body(struct lws_email *email, char *buf, int len)
  65. {
  66. struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
  67. struct lwsgs_fill_args a;
  68. char ss[150], esc[50];
  69. a.buf = buf;
  70. a.len = len;
  71. lws_snprintf(ss, sizeof(ss) - 1,
  72. "select content from email where username='%s';",
  73. lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
  74. strncpy(buf, "failed", len);
  75. if (sqlite3_exec(vhd->pdb, ss, lwsgs_lookup_callback_email, &a,
  76. NULL) != SQLITE_OK) {
  77. lwsl_err("Unable to lookup email: %s\n",
  78. sqlite3_errmsg(vhd->pdb));
  79. return 1;
  80. }
  81. return 0;
  82. }
  83. static int
  84. lwsgs_email_cb_sent(struct lws_email *email)
  85. {
  86. struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
  87. char s[200], esc[50];
  88. /* mark the user as having sent the verification email */
  89. lws_snprintf(s, sizeof(s) - 1,
  90. "update users set verified=1 where username='%s' and verified==0;",
  91. lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
  92. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  93. lwsl_err("%s: Unable to update user: %s\n", __func__,
  94. sqlite3_errmsg(vhd->pdb));
  95. return 1;
  96. }
  97. lws_snprintf(s, sizeof(s) - 1,
  98. "delete from email where username='%s';",
  99. lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
  100. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  101. lwsl_err("%s: Unable to delete email text: %s\n", __func__,
  102. sqlite3_errmsg(vhd->pdb));
  103. return 1;
  104. }
  105. return 0;
  106. }
  107. static int
  108. lwsgs_email_cb_on_next(struct lws_email *email)
  109. {
  110. struct per_vhost_data__gs *vhd = lws_container_of(email,
  111. struct per_vhost_data__gs, email);
  112. char s[LWSGS_EMAIL_CONTENT_SIZE], esc[50];
  113. time_t now = lws_now_secs();
  114. /*
  115. * users not verified in 24h get deleted
  116. */
  117. lws_snprintf(s, sizeof(s) - 1, "delete from users where ((verified != %d)"
  118. " and (creation_time <= %lu));", LWSGS_VERIFIED_ACCEPTED,
  119. (unsigned long)now - vhd->timeout_email_secs);
  120. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  121. lwsl_err("Unable to expire users: %s\n",
  122. sqlite3_errmsg(vhd->pdb));
  123. return 1;
  124. }
  125. lws_snprintf(s, sizeof(s) - 1, "update users set token_time=0 where "
  126. "(token_time <= %lu);",
  127. (unsigned long)now - vhd->timeout_email_secs);
  128. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  129. lwsl_err("Unable to expire users: %s\n",
  130. sqlite3_errmsg(vhd->pdb));
  131. return 1;
  132. }
  133. vhd->u.username[0] = '\0';
  134. lws_snprintf(s, sizeof(s) - 1, "select username from email limit 1;");
  135. if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &vhd->u,
  136. NULL) != SQLITE_OK) {
  137. lwsl_err("Unable to lookup user: %s\n", sqlite3_errmsg(vhd->pdb));
  138. return 1;
  139. }
  140. lws_snprintf(s, sizeof(s) - 1,
  141. "select username, creation_time, email, ip, verified, token"
  142. " from users where username='%s' limit 1;",
  143. lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
  144. if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &vhd->u,
  145. NULL) != SQLITE_OK) {
  146. lwsl_err("Unable to lookup user: %s\n",
  147. sqlite3_errmsg(vhd->pdb));
  148. return 1;
  149. }
  150. if (!vhd->u.username[0])
  151. /*
  152. * nothing to do, we are idle and no suitable
  153. * accounts waiting for verification. When a new user
  154. * is added we will get kicked to try again.
  155. */
  156. return 1;
  157. strncpy(email->email_to, vhd->u.email, sizeof(email->email_to) - 1);
  158. return 0;
  159. }
  160. struct lwsgs_subst_args
  161. {
  162. struct per_session_data__gs *pss;
  163. struct per_vhost_data__gs *vhd;
  164. struct lws *wsi;
  165. };
  166. static const char *
  167. lwsgs_subst(void *data, int index)
  168. {
  169. struct lwsgs_subst_args *a = (struct lwsgs_subst_args *)data;
  170. struct lwsgs_user u;
  171. lwsgw_hash sid;
  172. char esc[50], s[100];
  173. int n;
  174. a->pss->result[0] = '\0';
  175. u.email[0] = '\0';
  176. if (!lwsgs_get_sid_from_wsi(a->wsi, &sid)) {
  177. if (lwsgs_lookup_session(a->vhd, &sid, a->pss->result, 31)) {
  178. lwsl_notice("sid lookup for %s failed\n", sid.id);
  179. a->pss->delete_session = sid;
  180. return NULL;
  181. }
  182. lws_snprintf(s, sizeof(s) - 1, "select username,email "
  183. "from users where username = '%s';",
  184. lws_sql_purify(esc, a->pss->result, sizeof(esc) - 1));
  185. if (sqlite3_exec(a->vhd->pdb, s, lwsgs_lookup_callback_user,
  186. &u, NULL) != SQLITE_OK) {
  187. lwsl_err("Unable to lookup token: %s\n",
  188. sqlite3_errmsg(a->vhd->pdb));
  189. a->pss->delete_session = sid;
  190. return NULL;
  191. }
  192. } else
  193. lwsl_notice("no sid\n");
  194. strncpy(a->pss->result + 32, u.email, 100);
  195. switch (index) {
  196. case 0:
  197. return a->pss->result;
  198. case 1:
  199. n = lwsgs_get_auth_level(a->vhd, a->pss->result);
  200. sprintf(a->pss->result, "%d", n);
  201. return a->pss->result;
  202. case 2:
  203. return a->pss->result + 32;
  204. }
  205. return NULL;
  206. }
  207. static int
  208. callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
  209. void *user, void *in, size_t len)
  210. {
  211. struct per_session_data__gs *pss = (struct per_session_data__gs *)user;
  212. const struct lws_protocol_vhost_options *pvo;
  213. struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)
  214. lws_protocol_vh_priv_get(lws_get_vhost(wsi),
  215. &protocols[0]);
  216. char cookie[1024], username[32], *pc = cookie;
  217. unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
  218. struct lws_process_html_args *args;
  219. struct lws_session_info *sinfo;
  220. char s[LWSGS_EMAIL_CONTENT_SIZE];
  221. unsigned char *p, *start, *end;
  222. sqlite3_stmt *sm;
  223. lwsgw_hash sid;
  224. const char *cp;
  225. int n;
  226. switch (reason) {
  227. case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
  228. vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
  229. &protocols[0], sizeof(struct per_vhost_data__gs));
  230. if (!vhd)
  231. return 1;
  232. vhd->context = lws_get_context(wsi);
  233. /* defaults */
  234. vhd->timeout_idle_secs = 600;
  235. vhd->timeout_absolute_secs = 36000;
  236. vhd->timeout_anon_absolute_secs = 1200;
  237. vhd->timeout_email_secs = 24 * 3600;
  238. strcpy(vhd->email.email_helo, "unconfigured.com");
  239. strcpy(vhd->email.email_from, "noreply@unconfigured.com");
  240. strcpy(vhd->email_title, "Registration Email from unconfigured");
  241. strcpy(vhd->email.email_smtp_ip, "127.0.0.1");
  242. vhd->email.on_next = lwsgs_email_cb_on_next;
  243. vhd->email.on_get_body = lwsgs_email_cb_get_body;
  244. vhd->email.on_sent = lwsgs_email_cb_sent;
  245. vhd->email.data = (void *)vhd;
  246. pvo = (const struct lws_protocol_vhost_options *)in;
  247. while (pvo) {
  248. if (!strcmp(pvo->name, "admin-user"))
  249. strncpy(vhd->admin_user, pvo->value,
  250. sizeof(vhd->admin_user) - 1);
  251. if (!strcmp(pvo->name, "admin-password-sha1"))
  252. strncpy(vhd->admin_password_sha1.id, pvo->value,
  253. sizeof(vhd->admin_password_sha1.id) - 1);
  254. if (!strcmp(pvo->name, "session-db"))
  255. strncpy(vhd->session_db, pvo->value,
  256. sizeof(vhd->session_db) - 1);
  257. if (!strcmp(pvo->name, "confounder"))
  258. strncpy(vhd->confounder, pvo->value,
  259. sizeof(vhd->confounder) - 1);
  260. if (!strcmp(pvo->name, "email-from"))
  261. strncpy(vhd->email.email_from, pvo->value,
  262. sizeof(vhd->email.email_from) - 1);
  263. if (!strcmp(pvo->name, "email-helo"))
  264. strncpy(vhd->email.email_helo, pvo->value,
  265. sizeof(vhd->email.email_helo) - 1);
  266. if (!strcmp(pvo->name, "email-template"))
  267. strncpy(vhd->email_template, pvo->value,
  268. sizeof(vhd->email_template) - 1);
  269. if (!strcmp(pvo->name, "email-title"))
  270. strncpy(vhd->email_title, pvo->value,
  271. sizeof(vhd->email_title) - 1);
  272. if (!strcmp(pvo->name, "email-contact-person"))
  273. strncpy(vhd->email_contact_person, pvo->value,
  274. sizeof(vhd->email_contact_person) - 1);
  275. if (!strcmp(pvo->name, "email-confirm-url-base"))
  276. strncpy(vhd->email_confirm_url, pvo->value,
  277. sizeof(vhd->email_confirm_url) - 1);
  278. if (!strcmp(pvo->name, "email-server-ip"))
  279. strncpy(vhd->email.email_smtp_ip, pvo->value,
  280. sizeof(vhd->email.email_smtp_ip) - 1);
  281. if (!strcmp(pvo->name, "timeout-idle-secs"))
  282. vhd->timeout_idle_secs = atoi(pvo->value);
  283. if (!strcmp(pvo->name, "timeout-absolute-secs"))
  284. vhd->timeout_absolute_secs = atoi(pvo->value);
  285. if (!strcmp(pvo->name, "timeout-anon-absolute-secs"))
  286. vhd->timeout_anon_absolute_secs = atoi(pvo->value);
  287. if (!strcmp(pvo->name, "email-expire"))
  288. vhd->timeout_email_secs = atoi(pvo->value);
  289. pvo = pvo->next;
  290. }
  291. if (!vhd->admin_user[0] ||
  292. !vhd->admin_password_sha1.id[0] ||
  293. !vhd->session_db[0]) {
  294. lwsl_err("generic-sessions: "
  295. "You must give \"admin-user\", "
  296. "\"admin-password-sha1\", "
  297. "and \"session_db\" per-vhost options\n");
  298. return 1;
  299. }
  300. if (sqlite3_open_v2(vhd->session_db, &vhd->pdb,
  301. SQLITE_OPEN_READWRITE |
  302. SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
  303. lwsl_err("Unable to open session db %s: %s\n",
  304. vhd->session_db, sqlite3_errmsg(vhd->pdb));
  305. return 1;
  306. }
  307. if (sqlite3_prepare(vhd->pdb,
  308. "create table if not exists sessions ("
  309. " name char(40),"
  310. " username varchar(32),"
  311. " expire integer"
  312. ");",
  313. -1, &sm, NULL) != SQLITE_OK) {
  314. lwsl_err("Unable to prepare session table init: %s\n",
  315. sqlite3_errmsg(vhd->pdb));
  316. return 1;
  317. }
  318. if (sqlite3_step(sm) != SQLITE_DONE) {
  319. lwsl_err("Unable to run session table init: %s\n",
  320. sqlite3_errmsg(vhd->pdb));
  321. return 1;
  322. }
  323. sqlite3_finalize(sm);
  324. if (sqlite3_exec(vhd->pdb,
  325. "create table if not exists users ("
  326. " username varchar(32),"
  327. " creation_time integer,"
  328. " ip varchar(46),"
  329. " email varchar(100),"
  330. " pwhash varchar(42),"
  331. " pwsalt varchar(42),"
  332. " pwchange_time integer,"
  333. " token varchar(42),"
  334. " verified integer,"
  335. " token_time integer,"
  336. " last_forgot_validated integer,"
  337. " primary key (username)"
  338. ");",
  339. NULL, NULL, NULL) != SQLITE_OK) {
  340. lwsl_err("Unable to create user table: %s\n",
  341. sqlite3_errmsg(vhd->pdb));
  342. return 1;
  343. }
  344. sprintf(s, "create table if not exists email ("
  345. " username varchar(32),"
  346. " content blob,"
  347. " primary key (username)"
  348. ");");
  349. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  350. lwsl_err("Unable to create user table: %s\n",
  351. sqlite3_errmsg(vhd->pdb));
  352. return 1;
  353. }
  354. lws_email_init(&vhd->email, lws_uv_getloop(vhd->context, 0),
  355. LWSGS_EMAIL_CONTENT_SIZE);
  356. vhd->email_inited = 1;
  357. break;
  358. case LWS_CALLBACK_PROTOCOL_DESTROY:
  359. // lwsl_notice("gs: LWS_CALLBACK_PROTOCOL_DESTROY: v=%p, ctx=%p\n", vhd, vhd->context);
  360. if (vhd->pdb) {
  361. sqlite3_close(vhd->pdb);
  362. vhd->pdb = NULL;
  363. }
  364. if (vhd->email_inited) {
  365. lws_email_destroy(&vhd->email);
  366. vhd->email_inited = 0;
  367. }
  368. break;
  369. case LWS_CALLBACK_HTTP:
  370. lwsl_info("LWS_CALLBACK_HTTP: %s\n", (const char *)in);
  371. pss->login_session.id[0] = '\0';
  372. pss->phs.pos = 0;
  373. strncpy(pss->onward, (char *)in, sizeof(pss->onward) - 1);
  374. pss->onward[sizeof(pss->onward) - 1] = '\0';
  375. if (!strcmp((const char *)in, "/lwsgs-forgot")) {
  376. lwsgs_handler_forgot(vhd, wsi, pss);
  377. goto redirect_with_cookie;
  378. }
  379. if (!strcmp((const char *)in, "/lwsgs-confirm")) {
  380. lwsgs_handler_confirm(vhd, wsi, pss);
  381. goto redirect_with_cookie;
  382. }
  383. if (!strcmp((const char *)in, "/lwsgs-check")) {
  384. lwsgs_handler_check(vhd, wsi, pss);
  385. goto try_to_reuse;
  386. }
  387. if (!strcmp((const char *)in, "/lwsgs-login"))
  388. break;
  389. if (!strcmp((const char *)in, "/lwsgs-logout"))
  390. break;
  391. if (!strcmp((const char *)in, "/lwsgs-forgot"))
  392. break;
  393. if (!strcmp((const char *)in, "/lwsgs-change"))
  394. break;
  395. /* if no legitimate url for GET, return 404 */
  396. lwsl_err("http doing 404 on %s\n", (const char *)in);
  397. lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
  398. goto try_to_reuse;
  399. case LWS_CALLBACK_CHECK_ACCESS_RIGHTS:
  400. n = 0;
  401. username[0] = '\0';
  402. sid.id[0] = '\0';
  403. args = (struct lws_process_html_args *)in;
  404. lwsl_debug("LWS_CALLBACK_CHECK_ACCESS_RIGHTS\n");
  405. if (!lwsgs_get_sid_from_wsi(wsi, &sid)) {
  406. if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username))) {
  407. static const char * const oprot[] = {
  408. "http://", "https://"
  409. };
  410. lwsl_notice("session lookup for %s failed, probably expired\n", sid.id);
  411. pss->delete_session = sid;
  412. args->final = 1; /* signal we dealt with it */
  413. if (lws_hdr_copy(wsi, cookie, sizeof(cookie) - 1,
  414. WSI_TOKEN_HOST) < 0)
  415. return 1;
  416. lws_snprintf(pss->onward, sizeof(pss->onward) - 1,
  417. "%s%s%s", oprot[lws_is_ssl(wsi)],
  418. cookie, args->p);
  419. lwsl_notice("redirecting to ourselves with cookie refresh\n");
  420. /* we need a redirect to ourselves, session cookie is expired */
  421. goto redirect_with_cookie;
  422. }
  423. } else
  424. lwsl_notice("failed to get sid from wsi\n");
  425. n = lwsgs_get_auth_level(vhd, username);
  426. if ((args->max_len & n) != args->max_len) {
  427. lwsl_notice("Access rights fail 0x%X vs 0x%X (cookie %s)\n",
  428. args->max_len, n, sid.id);
  429. return 1;
  430. }
  431. lwsl_debug("Access rights OK\n");
  432. break;
  433. case LWS_CALLBACK_SESSION_INFO:
  434. {
  435. struct lwsgs_user u;
  436. sinfo = (struct lws_session_info *)in;
  437. sinfo->username[0] = '\0';
  438. sinfo->email[0] = '\0';
  439. sinfo->ip[0] = '\0';
  440. sinfo->session[0] = '\0';
  441. sinfo->mask = 0;
  442. sid.id[0] = '\0';
  443. lwsl_debug("LWS_CALLBACK_SESSION_INFO\n");
  444. if (lwsgs_get_sid_from_wsi(wsi, &sid))
  445. break;
  446. if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username)))
  447. break;
  448. lws_snprintf(s, sizeof(s) - 1,
  449. "select username, email from users where username='%s';",
  450. username);
  451. if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
  452. SQLITE_OK) {
  453. lwsl_err("Unable to lookup token: %s\n",
  454. sqlite3_errmsg(vhd->pdb));
  455. break;
  456. }
  457. strncpy(sinfo->username, u.username, sizeof(sinfo->username) - 1);
  458. sinfo->username[sizeof(sinfo->username) - 1] = '\0';
  459. strncpy(sinfo->email, u.email, sizeof(sinfo->email) - 1);
  460. strncpy(sinfo->session, sid.id, sizeof(sinfo->session) - 1);
  461. sinfo->mask = lwsgs_get_auth_level(vhd, username);
  462. lws_get_peer_simple(wsi, sinfo->ip, sizeof(sinfo->ip));
  463. }
  464. break;
  465. case LWS_CALLBACK_PROCESS_HTML:
  466. args = (struct lws_process_html_args *)in;
  467. {
  468. static const char * const vars[] = {
  469. "$lwsgs_user",
  470. "$lwsgs_auth",
  471. "$lwsgs_email"
  472. };
  473. struct lwsgs_subst_args a;
  474. a.vhd = vhd;
  475. a.pss = pss;
  476. a.wsi = wsi;
  477. pss->phs.vars = vars;
  478. pss->phs.count_vars = ARRAY_SIZE(vars);
  479. pss->phs.replace = lwsgs_subst;
  480. pss->phs.data = &a;
  481. if (lws_chunked_html_process(args, &pss->phs))
  482. return -1;
  483. }
  484. break;
  485. case LWS_CALLBACK_HTTP_BODY:
  486. if (len < 2)
  487. break;
  488. if (!pss->spa) {
  489. pss->spa = lws_spa_create(wsi, param_names,
  490. ARRAY_SIZE(param_names), 1024,
  491. NULL, NULL);
  492. if (!pss->spa)
  493. return -1;
  494. }
  495. if (lws_spa_process(pss->spa, in, len)) {
  496. lwsl_notice("spa process blew\n");
  497. return -1;
  498. }
  499. break;
  500. case LWS_CALLBACK_HTTP_BODY_COMPLETION:
  501. if (!pss->spa)
  502. break;
  503. lwsl_info("LWS_CALLBACK_HTTP_BODY_COMPLETION: %s\n", pss->onward);
  504. lws_spa_finalize(pss->spa);
  505. if (!strcmp((char *)pss->onward, "/lwsgs-change")) {
  506. if (!lwsgs_handler_change_password(vhd, wsi, pss)) {
  507. cp = lws_spa_get_string(pss->spa, FGS_GOOD);
  508. goto pass;
  509. }
  510. cp = lws_spa_get_string(pss->spa, FGS_BAD);
  511. lwsl_notice("user/password no good %s\n",
  512. lws_spa_get_string(pss->spa, FGS_USERNAME));
  513. strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
  514. pss->onward[sizeof(pss->onward) - 1] = '\0';
  515. goto completion_flow;
  516. }
  517. if (!strcmp((char *)pss->onward, "/lwsgs-login")) {
  518. if (lws_spa_get_string(pss->spa, FGS_FORGOT) &&
  519. lws_spa_get_string(pss->spa, FGS_FORGOT)[0]) {
  520. if (lwsgs_handler_forgot_pw_form(vhd, wsi, pss)) {
  521. n = FGS_FORGOT_BAD;
  522. goto reg_done;
  523. }
  524. /* get the email monitor to take a look */
  525. lws_email_check(&vhd->email);
  526. n = FGS_FORGOT_GOOD;
  527. goto reg_done;
  528. }
  529. if (!lws_spa_get_string(pss->spa, FGS_USERNAME) ||
  530. !lws_spa_get_string(pss->spa, FGS_PASSWORD)) {
  531. lwsl_notice("username '%s' or pw '%s' missing\n",
  532. lws_spa_get_string(pss->spa, FGS_USERNAME),
  533. lws_spa_get_string(pss->spa, FGS_PASSWORD));
  534. return -1;
  535. }
  536. if (lws_spa_get_string(pss->spa, FGS_REGISTER) &&
  537. lws_spa_get_string(pss->spa, FGS_REGISTER)[0]) {
  538. if (lwsgs_handler_register_form(vhd, wsi, pss))
  539. n = FGS_REG_BAD;
  540. else {
  541. n = FGS_REG_GOOD;
  542. /* get the email monitor to take a look */
  543. lws_email_check(&vhd->email);
  544. }
  545. reg_done:
  546. strncpy(pss->onward, lws_spa_get_string(pss->spa, n),
  547. sizeof(pss->onward) - 1);
  548. pss->onward[sizeof(pss->onward) - 1] = '\0';
  549. pss->login_expires = 0;
  550. pss->logging_out = 1;
  551. goto completion_flow;
  552. }
  553. /* we have the username and password... check if admin */
  554. if (lwsgw_check_admin(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
  555. lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
  556. if (lws_spa_get_string(pss->spa, FGS_ADMIN))
  557. cp = lws_spa_get_string(pss->spa, FGS_ADMIN);
  558. else
  559. if (lws_spa_get_string(pss->spa, FGS_GOOD))
  560. cp = lws_spa_get_string(pss->spa, FGS_GOOD);
  561. else {
  562. lwsl_info("No admin or good target url in form\n");
  563. return -1;
  564. }
  565. lwsl_debug("admin\n");
  566. goto pass;
  567. }
  568. /* check users in database */
  569. if (!lwsgs_check_credentials(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
  570. lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
  571. lwsl_info("pw hash check met\n");
  572. cp = lws_spa_get_string(pss->spa, FGS_GOOD);
  573. goto pass;
  574. } else
  575. lwsl_notice("user/password no good %s\n",
  576. lws_spa_get_string(pss->spa, FGS_USERNAME));
  577. if (!lws_spa_get_string(pss->spa, FGS_BAD)) {
  578. lwsl_info("No admin or good target url in form\n");
  579. return -1;
  580. }
  581. strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_BAD),
  582. sizeof(pss->onward) - 1);
  583. pss->onward[sizeof(pss->onward) - 1] = '\0';
  584. lwsl_debug("failed\n");
  585. goto completion_flow;
  586. }
  587. if (!strcmp((char *)pss->onward, "/lwsgs-logout")) {
  588. lwsl_notice("/logout\n");
  589. if (lwsgs_get_sid_from_wsi(wsi, &pss->login_session)) {
  590. lwsl_notice("not logged in...\n");
  591. return 1;
  592. }
  593. lwsgw_update_session(vhd, &pss->login_session, "");
  594. if (!lws_spa_get_string(pss->spa, FGS_GOOD)) {
  595. lwsl_info("No admin or good target url in form\n");
  596. return -1;
  597. }
  598. strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_GOOD), sizeof(pss->onward) - 1);
  599. pss->onward[sizeof(pss->onward) - 1] = '\0';
  600. pss->login_expires = 0;
  601. pss->logging_out = 1;
  602. goto completion_flow;
  603. }
  604. break;
  605. pass:
  606. strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
  607. pss->onward[sizeof(pss->onward) - 1] = '\0';
  608. if (lwsgs_get_sid_from_wsi(wsi, &sid))
  609. sid.id[0] = '\0';
  610. pss->login_expires = lws_now_secs() +
  611. vhd->timeout_absolute_secs;
  612. if (!sid.id[0]) {
  613. /* we need to create a new, authorized session */
  614. if (lwsgs_new_session_id(vhd, &pss->login_session,
  615. lws_spa_get_string(pss->spa, FGS_USERNAME),
  616. pss->login_expires))
  617. goto try_to_reuse;
  618. lwsl_info("Creating new session: %s\n",
  619. pss->login_session.id);
  620. } else {
  621. /*
  622. * we can just update the existing session to be
  623. * authorized
  624. */
  625. lwsl_info("Authorizing existing session %s", sid.id);
  626. lwsgw_update_session(vhd, &sid,
  627. lws_spa_get_string(pss->spa, FGS_USERNAME));
  628. pss->login_session = sid;
  629. }
  630. completion_flow:
  631. lwsgw_expire_old_sessions(vhd);
  632. goto redirect_with_cookie;
  633. case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
  634. if (pss && pss->spa) {
  635. lws_spa_destroy(pss->spa);
  636. pss->spa = NULL;
  637. }
  638. break;
  639. case LWS_CALLBACK_ADD_HEADERS:
  640. lwsgw_expire_old_sessions(vhd);
  641. args = (struct lws_process_html_args *)in;
  642. if (pss->delete_session.id[0]) {
  643. pc = cookie;
  644. lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
  645. cookie + sizeof(cookie) - 1);
  646. lwsl_info("deleting cookie '%s'\n", cookie);
  647. if (lws_add_http_header_by_name(wsi,
  648. (unsigned char *)"set-cookie:",
  649. (unsigned char *)cookie, pc - cookie,
  650. (unsigned char **)&args->p,
  651. (unsigned char *)args->p + args->max_len))
  652. return 1;
  653. }
  654. if (!pss->login_session.id[0])
  655. lwsgs_get_sid_from_wsi(wsi, &pss->login_session);
  656. if (!pss->login_session.id[0] && !pss->logging_out) {
  657. pss->login_expires = lws_now_secs() +
  658. vhd->timeout_anon_absolute_secs;
  659. if (lwsgs_new_session_id(vhd, &pss->login_session, "",
  660. pss->login_expires))
  661. goto try_to_reuse;
  662. pc = cookie;
  663. lwsgw_cookie_from_session(&pss->login_session,
  664. pss->login_expires, &pc,
  665. cookie + sizeof(cookie) - 1);
  666. lwsl_info("LWS_CALLBACK_ADD_HEADERS: setting cookie '%s'\n", cookie);
  667. if (lws_add_http_header_by_name(wsi,
  668. (unsigned char *)"set-cookie:",
  669. (unsigned char *)cookie, pc - cookie,
  670. (unsigned char **)&args->p,
  671. (unsigned char *)args->p + args->max_len))
  672. return 1;
  673. }
  674. break;
  675. default:
  676. break;
  677. }
  678. return 0;
  679. redirect_with_cookie:
  680. p = buffer + LWS_PRE;
  681. start = p;
  682. end = p + sizeof(buffer) - LWS_PRE;
  683. if (lws_add_http_header_status(wsi, HTTP_STATUS_SEE_OTHER, &p, end))
  684. return 1;
  685. if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
  686. (unsigned char *)pss->onward,
  687. strlen(pss->onward), &p, end))
  688. return 1;
  689. if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
  690. (unsigned char *)"text/html", 9, &p, end))
  691. return 1;
  692. if (lws_add_http_header_content_length(wsi, 0, &p, end))
  693. return 1;
  694. if (pss->delete_session.id[0]) {
  695. lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
  696. cookie + sizeof(cookie) - 1);
  697. lwsl_notice("deleting cookie '%s'\n", cookie);
  698. if (lws_add_http_header_by_name(wsi,
  699. (unsigned char *)"set-cookie:",
  700. (unsigned char *)cookie, pc - cookie,
  701. &p, end))
  702. return 1;
  703. }
  704. if (!pss->login_session.id[0]) {
  705. pss->login_expires = lws_now_secs() +
  706. vhd->timeout_anon_absolute_secs;
  707. if (lwsgs_new_session_id(vhd, &pss->login_session, "",
  708. pss->login_expires))
  709. return 1;
  710. } else
  711. pss->login_expires = lws_now_secs() +
  712. vhd->timeout_absolute_secs;
  713. if (pss->login_session.id[0] || pss->logging_out) {
  714. /*
  715. * we succeeded to login, we must issue a login
  716. * cookie with the prepared data
  717. */
  718. pc = cookie;
  719. lwsgw_cookie_from_session(&pss->login_session,
  720. pss->login_expires, &pc,
  721. cookie + sizeof(cookie) - 1);
  722. lwsl_info("setting cookie '%s'\n", cookie);
  723. pss->logging_out = 0;
  724. if (lws_add_http_header_by_name(wsi,
  725. (unsigned char *)"set-cookie:",
  726. (unsigned char *)cookie, pc - cookie,
  727. &p, end))
  728. return 1;
  729. }
  730. if (lws_finalize_http_header(wsi, &p, end))
  731. return 1;
  732. n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
  733. if (n < 0)
  734. return 1;
  735. /* fallthru */
  736. try_to_reuse:
  737. if (lws_http_transaction_completed(wsi))
  738. return -1;
  739. return 0;
  740. }
  741. static const struct lws_protocols protocols[] = {
  742. {
  743. "protocol-generic-sessions",
  744. callback_generic_sessions,
  745. sizeof(struct per_session_data__gs),
  746. 1024,
  747. },
  748. };
  749. LWS_EXTERN LWS_VISIBLE int
  750. init_protocol_generic_sessions(struct lws_context *context,
  751. struct lws_plugin_capability *c)
  752. {
  753. if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
  754. lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
  755. c->api_magic);
  756. return 1;
  757. }
  758. c->protocols = protocols;
  759. c->count_protocols = ARRAY_SIZE(protocols);
  760. c->extensions = NULL;
  761. c->count_extensions = 0;
  762. return 0;
  763. }
  764. LWS_EXTERN LWS_VISIBLE int
  765. destroy_protocol_generic_sessions(struct lws_context *context)
  766. {
  767. return 0;
  768. }