protocol-lws-messageboard
[platform/upstream/libwebsockets.git] / plugins / generic-sessions / utils.c
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 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
22 #include "private-lwsgs.h"
23
24 void
25 sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash)
26 {
27         static const char *hex = "0123456789abcdef";
28         char *p = shash->id;
29         int n;
30
31         for (n = 0; n < 20; n++) {
32                 *p++ = hex[hash[n] >> 4];
33                 *p++ = hex[hash[n] & 15];
34         }
35
36         *p = '\0';
37 }
38
39 int
40 lwsgw_check_admin(struct per_vhost_data__gs *vhd,
41                   const char *username, const char *password)
42 {
43         lwsgw_hash_bin hash_bin;
44         lwsgw_hash pw_hash;
45
46         if (strcmp(vhd->admin_user, username))
47                 return 0;
48
49         lws_SHA1((unsigned char *)password, strlen(password), hash_bin.bin);
50         sha1_to_lwsgw_hash(hash_bin.bin, &pw_hash);
51
52         return !strcmp(vhd->admin_password_sha1.id, pw_hash.id);
53 }
54
55 /*
56  * secure cookie: it can only be passed over https where it cannot be
57  *                snooped in transit
58  * HttpOnly:      it can only be accessed via http[s] transport, it cannot be
59  *                gotten at by JS
60  */
61 void
62 lwsgw_cookie_from_session(lwsgw_hash *sid, time_t expires, char **p, char *end)
63 {
64         struct tm *tm = gmtime(&expires);
65         time_t n = lws_now_secs();
66
67         *p += snprintf(*p, end - *p, "id=%s;Expires=", sid->id);
68 #ifdef WIN32
69         *p += strftime(*p, end - *p, "%Y %H:%M %Z", tm);
70 #else
71         *p += strftime(*p, end - *p, "%F %H:%M %Z", tm);
72 #endif
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");
77 }
78
79 int
80 lwsgw_expire_old_sessions(struct per_vhost_data__gs *vhd)
81 {
82         time_t n = lws_now_secs();
83         char s[200];
84
85         if (n - vhd->last_session_expire < 5)
86                 return 0;
87
88         vhd->last_session_expire = n;
89
90         snprintf(s, sizeof(s) - 1,
91                  "delete from sessions where "
92                  "expire <= %lu;", (unsigned long)n);
93
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));
97                 return 1;
98         }
99
100         return 0;
101 }
102
103 int
104 lwsgw_update_session(struct per_vhost_data__gs *vhd,
105                      lwsgw_hash *hash, const char *user)
106 {
107         time_t n = lws_now_secs();
108         char s[200], esc[50], esc1[50];
109
110         if (user[0])
111                 n += vhd->timeout_absolute_secs;
112         else
113                 n += vhd->timeout_anon_absolute_secs;
114
115         snprintf(s, sizeof(s) - 1,
116                  "update sessions set expire=%lu,username='%s' where name='%s';",
117                  (unsigned long)n,
118                  lws_sql_purify(esc, user, sizeof(esc)),
119                  lws_sql_purify(esc1, hash->id, sizeof(esc1)));
120
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));
124                 return 1;
125         }
126
127         return 0;
128 }
129
130 static int
131 lwsgw_session_from_cookie(const char *cookie, lwsgw_hash *sid)
132 {
133         const char *p = cookie;
134         int n;
135
136         while (*p) {
137                 if (p[0] == 'i' && p[1] == 'd' && p[2] == '=') {
138                         p += 3;
139                         break;
140                 }
141                 p++;
142         }
143         if (!*p) {
144                 lwsl_info("no id= in cookie\n");
145                 return 1;
146         }
147
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'))
152                         sid->id[n] = *p++;
153                 else {
154                         lwsl_info("bad chars in cookie id %c\n", *p);
155                         return 1;
156                 }
157         }
158
159         if (n < sizeof(sid->id) - 1) {
160                 lwsl_info("cookie id too short\n");
161                 return 1;
162         }
163
164         sid->id[sizeof(sid->id) - 1] = '\0';
165
166         return 0;
167 }
168
169 int
170 lwsgs_get_sid_from_wsi(struct lws *wsi, lwsgw_hash *sid)
171 {
172         char cookie[1024];
173
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__);
177                 return 1;
178         }
179         if (lws_hdr_copy(wsi, cookie, sizeof cookie, WSI_TOKEN_HTTP_COOKIE) < 0) {
180                 lwsl_info("cookie copy failed\n");
181                 return 1;
182         }
183         /* extract the sid from the cookie */
184         if (lwsgw_session_from_cookie(cookie, sid)) {
185                 lwsl_info("session from cookie failed\n");
186                 return 1;
187         }
188
189         return 0;
190 }
191
192 struct lla {
193         char *username;
194         int len;
195         int results;
196 };
197
198 static int
199 lwsgs_lookup_callback(void *priv, int cols, char **col_val, char **col_name)
200 {
201         struct lla *lla = (struct lla *)priv;
202
203         //lwsl_err("%s: %d\n", __func__, cols);
204
205         if (cols)
206                 lla->results = 0;
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);
211         }
212
213         return 0;
214 }
215
216 int
217 lwsgs_lookup_session(struct per_vhost_data__gs *vhd,
218                      const lwsgw_hash *sid, char *username, int len)
219 {
220         struct lla lla = { username, len, 1 };
221         char s[150], esc[50];
222
223         lwsgw_expire_old_sessions(vhd);
224
225         snprintf(s, sizeof(s) - 1,
226                  "select username from sessions where name = '%s';",
227                  lws_sql_purify(esc, sid->id, sizeof(esc) - 1));
228
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));
232
233                 return 1;
234         }
235
236         /* 0 if found */
237         return lla.results;
238 }
239
240 int
241 lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name)
242 {
243         struct lwsgs_user *u = (struct lwsgs_user *)priv;
244         int n;
245
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';
250                         continue;
251                 }
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';
255                         continue;
256                 }
257                 if (!strcmp(col_name[n], "creation_time")) {
258                         u->created = atol(col_val[n]);
259                         continue;
260                 }
261                 if (!strcmp(col_name[n], "last_forgot_validated")) {
262                         if (col_val[n])
263                                 u->last_forgot_validated = atol(col_val[n]);
264                         else
265                                 u->last_forgot_validated = 0;
266                         continue;
267                 }
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';
271                         continue;
272                 }
273                 if (!strcmp(col_name[n], "verified")) {
274                         u->verified = atoi(col_val[n]);
275                         continue;
276                 }
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';
280                         continue;
281                 }
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';
285                         continue;
286                 }
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';
290                         continue;
291                 }
292         }
293         return 0;
294 }
295
296 int
297 lwsgs_lookup_user(struct per_vhost_data__gs *vhd,
298                   const char *username, struct lwsgs_user *u)
299 {
300         char s[150], esc[50];
301
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));
307
308         if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, u, NULL) !=
309             SQLITE_OK) {
310                 lwsl_err("Unable to lookup user: %s\n",
311                          sqlite3_errmsg(vhd->pdb));
312
313                 return -1;
314         }
315
316         return !u->username[0];
317 }
318
319 int
320 lwsgs_new_session_id(struct per_vhost_data__gs *vhd,
321                      lwsgw_hash *sid, const char *username, int exp)
322 {
323         unsigned char sid_rand[20];
324         const char *u;
325         char s[300], esc[50], esc1[50];
326
327         if (username)
328                 u = username;
329         else
330                 u = "";
331
332         if (!sid)
333                 return 1;
334
335         memset(sid, 0, sizeof(*sid));
336
337         if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
338                            sizeof(sid_rand))
339                 return 1;
340
341         sha1_to_lwsgw_hash(sid_rand, sid);
342
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);
348
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));
352
353                 return 1;
354         }
355
356         return 0;
357 }
358
359 int
360 lwsgs_get_auth_level(struct per_vhost_data__gs *vhd,
361                      const char *username)
362 {
363         struct lwsgs_user u;
364         int n = 0;
365
366         /* we are logged in as some kind of user */
367         if (username[0]) {
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 */
372         }
373
374         if (!lwsgs_lookup_user(vhd, username, &u)) {
375                 if ((u.verified & 0xff) == LWSGS_VERIFIED_ACCEPTED)
376                         n |= LWSGS_AUTH_VERIFIED;
377
378                 if (u.last_forgot_validated > lws_now_secs() - 300)
379                         n |= LWSGS_AUTH_FORGOT_FLOW;
380         }
381
382         return n;
383 }
384
385 int
386 lwsgs_check_credentials(struct per_vhost_data__gs *vhd,
387                         const char *username, const char *password)
388 {
389         unsigned char buffer[300];
390         lwsgw_hash_bin hash_bin;
391         struct lwsgs_user u;
392         lwsgw_hash hash;
393         int n;
394
395         if (lwsgs_lookup_user(vhd, username, &u))
396                 return -1;
397
398         lwsl_info("user %s found, salt '%s'\n", username, u.pwsalt.id);
399
400         /* [password in ascii][salt] */
401         n = snprintf((char *)buffer, sizeof(buffer) - 1,
402                      "%s-%s-%s", password, vhd->confounder, u.pwsalt.id);
403
404         /* sha1sum of password + salt */
405         lws_SHA1(buffer, n, hash_bin.bin);
406         sha1_to_lwsgw_hash(&hash_bin.bin[0], &hash);
407
408         return !!strcmp(hash.id, u.pwhash.id);
409 }
410
411 /* sets u->pwsalt and u->pwhash */
412
413 int
414 lwsgs_hash_password(struct per_vhost_data__gs *vhd,
415                     const char *password, struct lwsgs_user *u)
416 {
417         lwsgw_hash_bin hash_bin;
418         lwsgw_hash hash;
419         unsigned char sid_rand[20];
420         unsigned char buffer[150];
421         int n;
422
423         /* create a random salt as big as the hash */
424
425         if (lws_get_random(vhd->context, sid_rand,
426                            sizeof(sid_rand)) !=
427                            sizeof(sid_rand)) {
428                 lwsl_err("Problem getting random for salt\n");
429                 return 1;
430         }
431         sha1_to_lwsgw_hash(sid_rand, &u->pwsalt);
432
433         if (lws_get_random(vhd->context, sid_rand,
434                            sizeof(sid_rand)) !=
435                            sizeof(sid_rand)) {
436                 lwsl_err("Problem getting random for token\n");
437                 return 1;
438         }
439         sha1_to_lwsgw_hash(sid_rand, &hash);
440
441         /* [password in ascii][salt] */
442         n = snprintf((char *)buffer, sizeof(buffer) - 1,
443                     "%s-%s-%s", password, vhd->confounder, u->pwsalt.id);
444
445         /* sha1sum of password + salt */
446         lws_SHA1(buffer, n, hash_bin.bin);
447         sha1_to_lwsgw_hash(&hash_bin.bin[0], &u->pwhash);
448
449         return 0;
450 }