utils.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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. void
  23. sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash)
  24. {
  25. static const char *hex = "0123456789abcdef";
  26. char *p = shash->id;
  27. int n;
  28. for (n = 0; n < 20; n++) {
  29. *p++ = hex[(hash[n] >> 4) & 0xf];
  30. *p++ = hex[hash[n] & 15];
  31. }
  32. *p = '\0';
  33. }
  34. int
  35. lwsgw_check_admin(struct per_vhost_data__gs *vhd,
  36. const char *username, const char *password)
  37. {
  38. lwsgw_hash_bin hash_bin;
  39. lwsgw_hash pw_hash;
  40. if (strcmp(vhd->admin_user, username))
  41. return 0;
  42. lws_SHA1((unsigned char *)password, strlen(password), hash_bin.bin);
  43. sha1_to_lwsgw_hash(hash_bin.bin, &pw_hash);
  44. return !strcmp(vhd->admin_password_sha1.id, pw_hash.id);
  45. }
  46. /*
  47. * secure cookie: it can only be passed over https where it cannot be
  48. * snooped in transit
  49. * HttpOnly: it can only be accessed via http[s] transport, it cannot be
  50. * gotten at by JS
  51. */
  52. void
  53. lwsgw_cookie_from_session(lwsgw_hash *sid, time_t expires, char **p, char *end)
  54. {
  55. struct tm *tm = gmtime(&expires);
  56. time_t n = lws_now_secs();
  57. *p += lws_snprintf(*p, end - *p, "id=%s;Expires=", sid->id);
  58. #ifdef WIN32
  59. *p += strftime(*p, end - *p, "%Y %H:%M %Z", tm);
  60. #else
  61. *p += strftime(*p, end - *p, "%F %H:%M %Z", tm);
  62. #endif
  63. *p += lws_snprintf(*p, end - *p, ";path=/");
  64. *p += lws_snprintf(*p, end - *p, ";Max-Age=%lu", (unsigned long)(expires - n));
  65. // *p += lws_snprintf(*p, end - *p, ";secure");
  66. *p += lws_snprintf(*p, end - *p, ";HttpOnly");
  67. }
  68. int
  69. lwsgw_expire_old_sessions(struct per_vhost_data__gs *vhd)
  70. {
  71. time_t n = lws_now_secs();
  72. char s[200];
  73. if (n - vhd->last_session_expire < 5)
  74. return 0;
  75. vhd->last_session_expire = n;
  76. lws_snprintf(s, sizeof(s) - 1,
  77. "delete from sessions where "
  78. "expire <= %lu;", (unsigned long)n);
  79. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  80. lwsl_err("Unable to expire sessions: %s\n",
  81. sqlite3_errmsg(vhd->pdb));
  82. return 1;
  83. }
  84. return 0;
  85. }
  86. int
  87. lwsgw_update_session(struct per_vhost_data__gs *vhd,
  88. lwsgw_hash *hash, const char *user)
  89. {
  90. time_t n = lws_now_secs();
  91. char s[200], esc[50], esc1[50];
  92. if (user[0])
  93. n += vhd->timeout_absolute_secs;
  94. else
  95. n += vhd->timeout_anon_absolute_secs;
  96. lws_snprintf(s, sizeof(s) - 1,
  97. "update sessions set expire=%lu,username='%s' where name='%s';",
  98. (unsigned long)n,
  99. lws_sql_purify(esc, user, sizeof(esc)),
  100. lws_sql_purify(esc1, hash->id, sizeof(esc1)));
  101. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  102. lwsl_err("Unable to update session: %s\n",
  103. sqlite3_errmsg(vhd->pdb));
  104. return 1;
  105. }
  106. return 0;
  107. }
  108. static int
  109. lwsgw_session_from_cookie(const char *cookie, lwsgw_hash *sid)
  110. {
  111. const char *p = cookie;
  112. int n;
  113. while (*p) {
  114. if (p[0] == 'i' && p[1] == 'd' && p[2] == '=') {
  115. p += 3;
  116. break;
  117. }
  118. p++;
  119. }
  120. if (!*p) {
  121. lwsl_info("no id= in cookie\n");
  122. return 1;
  123. }
  124. for (n = 0; n < sizeof(sid->id) - 1 && *p; n++) {
  125. /* our SID we issue only has these chars */
  126. if ((*p >= '0' && *p <= '9') ||
  127. (*p >= 'a' && *p <= 'f'))
  128. sid->id[n] = *p++;
  129. else {
  130. lwsl_info("bad chars in cookie id %c\n", *p);
  131. return 1;
  132. }
  133. }
  134. if (n < sizeof(sid->id) - 1) {
  135. lwsl_info("cookie id too short\n");
  136. return 1;
  137. }
  138. sid->id[sizeof(sid->id) - 1] = '\0';
  139. return 0;
  140. }
  141. int
  142. lwsgs_get_sid_from_wsi(struct lws *wsi, lwsgw_hash *sid)
  143. {
  144. char cookie[1024];
  145. /* fail it on no cookie */
  146. if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
  147. lwsl_info("%s: no cookie\n", __func__);
  148. return 1;
  149. }
  150. if (lws_hdr_copy(wsi, cookie, sizeof cookie, WSI_TOKEN_HTTP_COOKIE) < 0) {
  151. lwsl_info("cookie copy failed\n");
  152. return 1;
  153. }
  154. /* extract the sid from the cookie */
  155. if (lwsgw_session_from_cookie(cookie, sid)) {
  156. lwsl_info("session from cookie failed\n");
  157. return 1;
  158. }
  159. return 0;
  160. }
  161. struct lla {
  162. char *username;
  163. int len;
  164. int results;
  165. };
  166. static int
  167. lwsgs_lookup_callback(void *priv, int cols, char **col_val, char **col_name)
  168. {
  169. struct lla *lla = (struct lla *)priv;
  170. //lwsl_err("%s: %d\n", __func__, cols);
  171. if (cols)
  172. lla->results = 0;
  173. if (col_val && col_val[0]) {
  174. strncpy(lla->username, col_val[0], lla->len);
  175. lla->username[lla->len - 1] = '\0';
  176. lwsl_info("%s: %s\n", __func__, lla->username);
  177. }
  178. return 0;
  179. }
  180. int
  181. lwsgs_lookup_session(struct per_vhost_data__gs *vhd,
  182. const lwsgw_hash *sid, char *username, int len)
  183. {
  184. struct lla lla = { username, len, 1 };
  185. char s[150], esc[50];
  186. lwsgw_expire_old_sessions(vhd);
  187. lws_snprintf(s, sizeof(s) - 1,
  188. "select username from sessions where name = '%s';",
  189. lws_sql_purify(esc, sid->id, sizeof(esc) - 1));
  190. if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback, &lla, NULL) != SQLITE_OK) {
  191. lwsl_err("Unable to create user table: %s\n",
  192. sqlite3_errmsg(vhd->pdb));
  193. return 1;
  194. }
  195. /* 0 if found */
  196. return lla.results;
  197. }
  198. int
  199. lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name)
  200. {
  201. struct lwsgs_user *u = (struct lwsgs_user *)priv;
  202. int n;
  203. for (n = 0; n < cols; n++) {
  204. if (!strcmp(col_name[n], "username")) {
  205. strncpy(u->username, col_val[n], sizeof(u->username) - 1);
  206. u->username[sizeof(u->username) - 1] = '\0';
  207. continue;
  208. }
  209. if (!strcmp(col_name[n], "ip")) {
  210. strncpy(u->ip, col_val[n], sizeof(u->ip) - 1);
  211. u->ip[sizeof(u->ip) - 1] = '\0';
  212. continue;
  213. }
  214. if (!strcmp(col_name[n], "creation_time")) {
  215. u->created = atol(col_val[n]);
  216. continue;
  217. }
  218. if (!strcmp(col_name[n], "last_forgot_validated")) {
  219. if (col_val[n])
  220. u->last_forgot_validated = atol(col_val[n]);
  221. else
  222. u->last_forgot_validated = 0;
  223. continue;
  224. }
  225. if (!strcmp(col_name[n], "email")) {
  226. strncpy(u->email, col_val[n], sizeof(u->email) - 1);
  227. u->email[sizeof(u->email) - 1] = '\0';
  228. continue;
  229. }
  230. if (!strcmp(col_name[n], "verified")) {
  231. u->verified = atoi(col_val[n]);
  232. continue;
  233. }
  234. if (!strcmp(col_name[n], "pwhash")) {
  235. strncpy(u->pwhash.id, col_val[n], sizeof(u->pwhash.id) - 1);
  236. u->pwhash.id[sizeof(u->pwhash.id) - 1] = '\0';
  237. continue;
  238. }
  239. if (!strcmp(col_name[n], "pwsalt")) {
  240. strncpy(u->pwsalt.id, col_val[n], sizeof(u->pwsalt.id) - 1);
  241. u->pwsalt.id[sizeof(u->pwsalt.id) - 1] = '\0';
  242. continue;
  243. }
  244. if (!strcmp(col_name[n], "token")) {
  245. strncpy(u->token.id, col_val[n], sizeof(u->token.id) - 1);
  246. u->token.id[sizeof(u->token.id) - 1] = '\0';
  247. continue;
  248. }
  249. }
  250. return 0;
  251. }
  252. int
  253. lwsgs_lookup_user(struct per_vhost_data__gs *vhd,
  254. const char *username, struct lwsgs_user *u)
  255. {
  256. char s[150], esc[50];
  257. u->username[0] = '\0';
  258. lws_snprintf(s, sizeof(s) - 1,
  259. "select username,creation_time,ip,email,verified,pwhash,pwsalt,last_forgot_validated "
  260. "from users where username = '%s';",
  261. lws_sql_purify(esc, username, sizeof(esc) - 1));
  262. if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, u, NULL) !=
  263. SQLITE_OK) {
  264. lwsl_err("Unable to lookup user: %s\n",
  265. sqlite3_errmsg(vhd->pdb));
  266. return -1;
  267. }
  268. return !u->username[0];
  269. }
  270. int
  271. lwsgs_new_session_id(struct per_vhost_data__gs *vhd,
  272. lwsgw_hash *sid, const char *username, int exp)
  273. {
  274. unsigned char sid_rand[20];
  275. const char *u;
  276. char s[300], esc[50], esc1[50];
  277. if (username)
  278. u = username;
  279. else
  280. u = "";
  281. if (!sid)
  282. return 1;
  283. memset(sid, 0, sizeof(*sid));
  284. if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
  285. sizeof(sid_rand))
  286. return 1;
  287. sha1_to_lwsgw_hash(sid_rand, sid);
  288. lws_snprintf(s, sizeof(s) - 1,
  289. "insert into sessions(name, username, expire) "
  290. "values ('%s', '%s', %u);",
  291. lws_sql_purify(esc, sid->id, sizeof(esc) - 1),
  292. lws_sql_purify(esc1, u, sizeof(esc1) - 1), exp);
  293. if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
  294. lwsl_err("Unable to insert session: %s\n",
  295. sqlite3_errmsg(vhd->pdb));
  296. return 1;
  297. }
  298. return 0;
  299. }
  300. int
  301. lwsgs_get_auth_level(struct per_vhost_data__gs *vhd,
  302. const char *username)
  303. {
  304. struct lwsgs_user u;
  305. int n = 0;
  306. /* we are logged in as some kind of user */
  307. if (username[0]) {
  308. n |= LWSGS_AUTH_LOGGED_IN;
  309. /* we are logged in as admin */
  310. if (!strcmp(username, vhd->admin_user))
  311. n |= LWSGS_AUTH_VERIFIED | LWSGS_AUTH_ADMIN; /* automatically verified */
  312. }
  313. if (!lwsgs_lookup_user(vhd, username, &u)) {
  314. if ((u.verified & 0xff) == LWSGS_VERIFIED_ACCEPTED)
  315. n |= LWSGS_AUTH_VERIFIED;
  316. if (u.last_forgot_validated > lws_now_secs() - 300)
  317. n |= LWSGS_AUTH_FORGOT_FLOW;
  318. }
  319. return n;
  320. }
  321. int
  322. lwsgs_check_credentials(struct per_vhost_data__gs *vhd,
  323. const char *username, const char *password)
  324. {
  325. unsigned char buffer[300];
  326. lwsgw_hash_bin hash_bin;
  327. struct lwsgs_user u;
  328. lwsgw_hash hash;
  329. int n;
  330. if (lwsgs_lookup_user(vhd, username, &u))
  331. return -1;
  332. lwsl_info("user %s found, salt '%s'\n", username, u.pwsalt.id);
  333. /* [password in ascii][salt] */
  334. n = lws_snprintf((char *)buffer, sizeof(buffer) - 1,
  335. "%s-%s-%s", password, vhd->confounder, u.pwsalt.id);
  336. /* sha1sum of password + salt */
  337. lws_SHA1(buffer, n, hash_bin.bin);
  338. sha1_to_lwsgw_hash(&hash_bin.bin[0], &hash);
  339. return !!strcmp(hash.id, u.pwhash.id);
  340. }
  341. /* sets u->pwsalt and u->pwhash */
  342. int
  343. lwsgs_hash_password(struct per_vhost_data__gs *vhd,
  344. const char *password, struct lwsgs_user *u)
  345. {
  346. lwsgw_hash_bin hash_bin;
  347. lwsgw_hash hash;
  348. unsigned char sid_rand[20];
  349. unsigned char buffer[150];
  350. int n;
  351. /* create a random salt as big as the hash */
  352. if (lws_get_random(vhd->context, sid_rand,
  353. sizeof(sid_rand)) !=
  354. sizeof(sid_rand)) {
  355. lwsl_err("Problem getting random for salt\n");
  356. return 1;
  357. }
  358. sha1_to_lwsgw_hash(sid_rand, &u->pwsalt);
  359. if (lws_get_random(vhd->context, sid_rand,
  360. sizeof(sid_rand)) !=
  361. sizeof(sid_rand)) {
  362. lwsl_err("Problem getting random for token\n");
  363. return 1;
  364. }
  365. sha1_to_lwsgw_hash(sid_rand, &hash);
  366. /* [password in ascii][salt] */
  367. n = lws_snprintf((char *)buffer, sizeof(buffer) - 1,
  368. "%s-%s-%s", password, vhd->confounder, u->pwsalt.id);
  369. /* sha1sum of password + salt */
  370. lws_SHA1(buffer, n, hash_bin.bin);
  371. sha1_to_lwsgw_hash(&hash_bin.bin[0], &u->pwhash);
  372. return 0;
  373. }