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"
25 sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash)
27 static const char *hex = "0123456789abcdef";
31 for (n = 0; n < 20; n++) {
32 *p++ = hex[hash[n] >> 4];
33 *p++ = hex[hash[n] & 15];
40 lwsgw_check_admin(struct per_vhost_data__gs *vhd,
41 const char *username, const char *password)
43 lwsgw_hash_bin hash_bin;
46 if (strcmp(vhd->admin_user, username))
49 lws_SHA1((unsigned char *)password, strlen(password), hash_bin.bin);
50 sha1_to_lwsgw_hash(hash_bin.bin, &pw_hash);
52 return !strcmp(vhd->admin_password_sha1.id, pw_hash.id);
56 * secure cookie: it can only be passed over https where it cannot be
58 * HttpOnly: it can only be accessed via http[s] transport, it cannot be
62 lwsgw_cookie_from_session(lwsgw_hash *sid, time_t expires, char **p, char *end)
64 struct tm *tm = gmtime(&expires);
65 time_t n = lws_now_secs();
67 *p += snprintf(*p, end - *p, "id=%s;Expires=", sid->id);
69 *p += strftime(*p, end - *p, "%Y %H:%M %Z", tm);
71 *p += strftime(*p, end - *p, "%F %H:%M %Z", tm);
73 *p += snprintf(*p, end - *p, ";path=/");
74 *p += snprintf(*p, end - *p, ";Max-Age=%lu", (unsigned long)(expires - n));
75 // *p += snprintf(*p, end - *p, ";secure");
76 *p += snprintf(*p, end - *p, ";HttpOnly");
80 lwsgw_expire_old_sessions(struct per_vhost_data__gs *vhd)
82 time_t n = lws_now_secs();
85 if (n - vhd->last_session_expire < 5)
88 vhd->last_session_expire = n;
90 snprintf(s, sizeof(s) - 1,
91 "delete from sessions where "
92 "expire <= %lu;", (unsigned long)n);
94 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
95 lwsl_err("Unable to expire sessions: %s\n",
96 sqlite3_errmsg(vhd->pdb));
104 lwsgw_update_session(struct per_vhost_data__gs *vhd,
105 lwsgw_hash *hash, const char *user)
107 time_t n = lws_now_secs();
108 char s[200], esc[50], esc1[50];
111 n += vhd->timeout_absolute_secs;
113 n += vhd->timeout_anon_absolute_secs;
115 snprintf(s, sizeof(s) - 1,
116 "update sessions set expire=%lu,username='%s' where name='%s';",
118 lws_sql_purify(esc, user, sizeof(esc)),
119 lws_sql_purify(esc1, hash->id, sizeof(esc1)));
121 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
122 lwsl_err("Unable to update session: %s\n",
123 sqlite3_errmsg(vhd->pdb));
131 lwsgw_session_from_cookie(const char *cookie, lwsgw_hash *sid)
133 const char *p = cookie;
137 if (p[0] == 'i' && p[1] == 'd' && p[2] == '=') {
144 lwsl_info("no id= in cookie\n");
148 for (n = 0; n < sizeof(sid->id) - 1 && *p; n++) {
149 /* our SID we issue only has these chars */
150 if ((*p >= '0' && *p <= '9') ||
151 (*p >= 'a' && *p <= 'f'))
154 lwsl_info("bad chars in cookie id %c\n", *p);
159 if (n < sizeof(sid->id) - 1) {
160 lwsl_info("cookie id too short\n");
164 sid->id[sizeof(sid->id) - 1] = '\0';
170 lwsgs_get_sid_from_wsi(struct lws *wsi, lwsgw_hash *sid)
174 /* fail it on no cookie */
175 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
176 lwsl_info("%s: no cookie\n", __func__);
179 if (lws_hdr_copy(wsi, cookie, sizeof cookie, WSI_TOKEN_HTTP_COOKIE) < 0) {
180 lwsl_info("cookie copy failed\n");
183 /* extract the sid from the cookie */
184 if (lwsgw_session_from_cookie(cookie, sid)) {
185 lwsl_info("session from cookie failed\n");
199 lwsgs_lookup_callback(void *priv, int cols, char **col_val, char **col_name)
201 struct lla *lla = (struct lla *)priv;
203 //lwsl_err("%s: %d\n", __func__, cols);
207 if (col_val && col_val[0]) {
208 strncpy(lla->username, col_val[0], lla->len);
209 lla->username[lla->len - 1] = '\0';
210 lwsl_info("%s: %s\n", __func__, lla->username);
217 lwsgs_lookup_session(struct per_vhost_data__gs *vhd,
218 const lwsgw_hash *sid, char *username, int len)
220 struct lla lla = { username, len, 1 };
221 char s[150], esc[50];
223 lwsgw_expire_old_sessions(vhd);
225 snprintf(s, sizeof(s) - 1,
226 "select username from sessions where name = '%s';",
227 lws_sql_purify(esc, sid->id, sizeof(esc) - 1));
229 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback, &lla, NULL) != SQLITE_OK) {
230 lwsl_err("Unable to create user table: %s\n",
231 sqlite3_errmsg(vhd->pdb));
241 lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name)
243 struct lwsgs_user *u = (struct lwsgs_user *)priv;
246 for (n = 0; n < cols; n++) {
247 if (!strcmp(col_name[n], "username")) {
248 strncpy(u->username, col_val[n], sizeof(u->username) - 1);
249 u->username[sizeof(u->username) - 1] = '\0';
252 if (!strcmp(col_name[n], "ip")) {
253 strncpy(u->ip, col_val[n], sizeof(u->ip) - 1);
254 u->ip[sizeof(u->ip) - 1] = '\0';
257 if (!strcmp(col_name[n], "creation_time")) {
258 u->created = atol(col_val[n]);
261 if (!strcmp(col_name[n], "last_forgot_validated")) {
263 u->last_forgot_validated = atol(col_val[n]);
265 u->last_forgot_validated = 0;
268 if (!strcmp(col_name[n], "email")) {
269 strncpy(u->email, col_val[n], sizeof(u->email) - 1);
270 u->email[sizeof(u->email) - 1] = '\0';
273 if (!strcmp(col_name[n], "verified")) {
274 u->verified = atoi(col_val[n]);
277 if (!strcmp(col_name[n], "pwhash")) {
278 strncpy(u->pwhash.id, col_val[n], sizeof(u->pwhash.id) - 1);
279 u->pwhash.id[sizeof(u->pwhash.id) - 1] = '\0';
282 if (!strcmp(col_name[n], "pwsalt")) {
283 strncpy(u->pwsalt.id, col_val[n], sizeof(u->pwsalt.id) - 1);
284 u->pwsalt.id[sizeof(u->pwsalt.id) - 1] = '\0';
287 if (!strcmp(col_name[n], "token")) {
288 strncpy(u->token.id, col_val[n], sizeof(u->token.id) - 1);
289 u->token.id[sizeof(u->token.id) - 1] = '\0';
297 lwsgs_lookup_user(struct per_vhost_data__gs *vhd,
298 const char *username, struct lwsgs_user *u)
300 char s[150], esc[50];
302 u->username[0] = '\0';
303 snprintf(s, sizeof(s) - 1,
304 "select username,creation_time,ip,email,verified,pwhash,pwsalt,last_forgot_validated "
305 "from users where username = '%s';",
306 lws_sql_purify(esc, username, sizeof(esc) - 1));
308 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, u, NULL) !=
310 lwsl_err("Unable to lookup user: %s\n",
311 sqlite3_errmsg(vhd->pdb));
316 return !u->username[0];
320 lwsgs_new_session_id(struct per_vhost_data__gs *vhd,
321 lwsgw_hash *sid, const char *username, int exp)
323 unsigned char sid_rand[20];
325 char s[300], esc[50], esc1[50];
335 memset(sid, 0, sizeof(*sid));
337 if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
341 sha1_to_lwsgw_hash(sid_rand, sid);
343 snprintf(s, sizeof(s) - 1,
344 "insert into sessions(name, username, expire) "
345 "values ('%s', '%s', %u);",
346 lws_sql_purify(esc, sid->id, sizeof(esc) - 1),
347 lws_sql_purify(esc1, u, sizeof(esc1) - 1), exp);
349 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
350 lwsl_err("Unable to insert session: %s\n",
351 sqlite3_errmsg(vhd->pdb));
360 lwsgs_get_auth_level(struct per_vhost_data__gs *vhd,
361 const char *username)
366 /* we are logged in as some kind of user */
368 n |= LWSGS_AUTH_LOGGED_IN;
369 /* we are logged in as admin */
370 if (!strcmp(username, vhd->admin_user))
371 n |= LWSGS_AUTH_VERIFIED | LWSGS_AUTH_ADMIN; /* automatically verified */
374 if (!lwsgs_lookup_user(vhd, username, &u)) {
375 if ((u.verified & 0xff) == LWSGS_VERIFIED_ACCEPTED)
376 n |= LWSGS_AUTH_VERIFIED;
378 if (u.last_forgot_validated > lws_now_secs() - 300)
379 n |= LWSGS_AUTH_FORGOT_FLOW;
386 lwsgs_check_credentials(struct per_vhost_data__gs *vhd,
387 const char *username, const char *password)
389 unsigned char buffer[300];
390 lwsgw_hash_bin hash_bin;
395 if (lwsgs_lookup_user(vhd, username, &u))
398 lwsl_info("user %s found, salt '%s'\n", username, u.pwsalt.id);
400 /* [password in ascii][salt] */
401 n = snprintf((char *)buffer, sizeof(buffer) - 1,
402 "%s-%s-%s", password, vhd->confounder, u.pwsalt.id);
404 /* sha1sum of password + salt */
405 lws_SHA1(buffer, n, hash_bin.bin);
406 sha1_to_lwsgw_hash(&hash_bin.bin[0], &hash);
408 return !!strcmp(hash.id, u.pwhash.id);
411 /* sets u->pwsalt and u->pwhash */
414 lwsgs_hash_password(struct per_vhost_data__gs *vhd,
415 const char *password, struct lwsgs_user *u)
417 lwsgw_hash_bin hash_bin;
419 unsigned char sid_rand[20];
420 unsigned char buffer[150];
423 /* create a random salt as big as the hash */
425 if (lws_get_random(vhd->context, sid_rand,
428 lwsl_err("Problem getting random for salt\n");
431 sha1_to_lwsgw_hash(sid_rand, &u->pwsalt);
433 if (lws_get_random(vhd->context, sid_rand,
436 lwsl_err("Problem getting random for token\n");
439 sha1_to_lwsgw_hash(sid_rand, &hash);
441 /* [password in ascii][salt] */
442 n = snprintf((char *)buffer, sizeof(buffer) - 1,
443 "%s-%s-%s", password, vhd->confounder, u->pwsalt.id);
445 /* sha1sum of password + salt */
446 lws_SHA1(buffer, n, hash_bin.bin);
447 sha1_to_lwsgw_hash(&hash_bin.bin[0], &u->pwhash);