1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include "private-lwsgs.h"
26 #include <stdlib.h>
27
28 void
sha256_to_lwsgw_hash(unsigned char * hash,lwsgw_hash * shash)29 sha256_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash)
30 {
31 static const char *hex = "0123456789abcdef";
32 char *p = shash->id;
33 int n;
34
35 for (n = 0; n < (int)lws_genhash_size(LWS_GENHASH_TYPE_SHA256); n++) {
36 *p++ = hex[(hash[n] >> 4) & 0xf];
37 *p++ = hex[hash[n] & 15];
38 }
39
40 *p = '\0';
41 }
42
43 int
lwsgw_check_admin(struct per_vhost_data__gs * vhd,const char * username,const char * password)44 lwsgw_check_admin(struct per_vhost_data__gs *vhd,
45 const char *username, const char *password)
46 {
47 lwsgw_hash_bin hash_bin;
48 lwsgw_hash pw_hash;
49
50 if (strcmp(vhd->admin_user, username))
51 return 0;
52
53 lws_SHA1((unsigned char *)password, strlen(password), hash_bin.bin);
54 sha256_to_lwsgw_hash(hash_bin.bin, &pw_hash);
55
56 return !strcmp(vhd->admin_password_sha256.id, pw_hash.id);
57 }
58
59 /*
60 * secure cookie: it can only be passed over https where it cannot be
61 * snooped in transit
62 * HttpOnly: it can only be accessed via http[s] transport, it cannot be
63 * gotten at by JS
64 */
65 void
lwsgw_cookie_from_session(lwsgw_hash * sid,time_t expires,char ** p,char * end)66 lwsgw_cookie_from_session(lwsgw_hash *sid, time_t expires, char **p, char *end)
67 {
68 struct tm *tm = gmtime(&expires);
69 time_t n = lws_now_secs();
70
71 *p += lws_snprintf(*p, end - *p, "id=%s;Expires=", sid->id);
72 #ifdef WIN32
73 *p += strftime(*p, end - *p, "%Y %H:%M %Z", tm);
74 #else
75 *p += strftime(*p, end - *p, "%F %H:%M %Z", tm);
76 #endif
77 *p += lws_snprintf(*p, end - *p, ";path=/");
78 *p += lws_snprintf(*p, end - *p, ";Max-Age=%lu", (unsigned long)(expires - n));
79 // *p += lws_snprintf(*p, end - *p, ";secure");
80 *p += lws_snprintf(*p, end - *p, ";HttpOnly");
81 }
82
83 int
lwsgw_expire_old_sessions(struct per_vhost_data__gs * vhd)84 lwsgw_expire_old_sessions(struct per_vhost_data__gs *vhd)
85 {
86 time_t n = lws_now_secs();
87 char s[200];
88
89 if (n - vhd->last_session_expire < 5)
90 return 0;
91
92 vhd->last_session_expire = n;
93
94 lws_snprintf(s, sizeof(s) - 1,
95 "delete from sessions where "
96 "expire <= %lu;", (unsigned long)n);
97
98 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
99 lwsl_err("Unable to expire sessions: %s\n",
100 sqlite3_errmsg(vhd->pdb));
101 return 1;
102 }
103
104 return 0;
105 }
106
107 int
lwsgw_update_session(struct per_vhost_data__gs * vhd,lwsgw_hash * hash,const char * user)108 lwsgw_update_session(struct per_vhost_data__gs *vhd,
109 lwsgw_hash *hash, const char *user)
110 {
111 time_t n = lws_now_secs();
112 char s[200], esc[96], esc1[96];
113
114 if (user[0])
115 n += vhd->timeout_absolute_secs;
116 else
117 n += vhd->timeout_anon_absolute_secs;
118
119 lws_snprintf(s, sizeof(s) - 1,
120 "update sessions set expire=%lu,username='%s' where name='%s';",
121 (unsigned long)n,
122 lws_sql_purify(esc, user, sizeof(esc)),
123 lws_sql_purify(esc1, hash->id, sizeof(esc1)));
124
125 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
126 lwsl_err("Unable to update session: %s\n",
127 sqlite3_errmsg(vhd->pdb));
128 return 1;
129 }
130
131 puts(s);
132
133 return 0;
134 }
135
136 static int
lwsgw_session_from_cookie(const char * cookie,lwsgw_hash * sid)137 lwsgw_session_from_cookie(const char *cookie, lwsgw_hash *sid)
138 {
139 const char *p = cookie;
140 int n;
141
142 while (*p) {
143 if (p[0] == 'i' && p[1] == 'd' && p[2] == '=') {
144 p += 3;
145 break;
146 }
147 p++;
148 }
149 if (!*p) {
150 lwsl_info("no id= in cookie\n");
151 return 1;
152 }
153
154 for (n = 0; n < (int)sizeof(sid->id) - 1 && *p; n++) {
155 /* our SID we issue only has these chars */
156 if ((*p >= '0' && *p <= '9') ||
157 (*p >= 'a' && *p <= 'f'))
158 sid->id[n] = *p++;
159 else {
160 lwsl_info("bad chars in cookie id %c\n", *p);
161 return 1;
162 }
163 }
164
165 if (n < (int)sizeof(sid->id) - 1) {
166 lwsl_info("cookie id too short\n");
167 return 1;
168 }
169
170 sid->id[sizeof(sid->id) - 1] = '\0';
171
172 return 0;
173 }
174
175 int
lwsgs_get_sid_from_wsi(struct lws * wsi,lwsgw_hash * sid)176 lwsgs_get_sid_from_wsi(struct lws *wsi, lwsgw_hash *sid)
177 {
178 char cookie[1024];
179
180 /* fail it on no cookie */
181 if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
182 lwsl_info("%s: no cookie\n", __func__);
183 return 1;
184 }
185 if (lws_hdr_copy(wsi, cookie, sizeof cookie, WSI_TOKEN_HTTP_COOKIE) < 0) {
186 lwsl_info("cookie copy failed\n");
187 return 1;
188 }
189 /* extract the sid from the cookie */
190 if (lwsgw_session_from_cookie(cookie, sid)) {
191 lwsl_info("%s: session from cookie failed\n", __func__);
192 return 1;
193 }
194
195 return 0;
196 }
197
198 struct lla {
199 char *username;
200 int len;
201 int results;
202 };
203
204 static int
lwsgs_lookup_callback(void * priv,int cols,char ** col_val,char ** col_name)205 lwsgs_lookup_callback(void *priv, int cols, char **col_val, char **col_name)
206 {
207 struct lla *lla = (struct lla *)priv;
208
209 //lwsl_err("%s: %d\n", __func__, cols);
210
211 if (cols)
212 lla->results = 0;
213 if (col_val && col_val[0]) {
214 lws_strncpy(lla->username, col_val[0], lla->len + 1);
215 lwsl_info("%s: %s\n", __func__, lla->username);
216 }
217
218 return 0;
219 }
220
221 int
lwsgs_lookup_session(struct per_vhost_data__gs * vhd,const lwsgw_hash * sid,char * username,int len)222 lwsgs_lookup_session(struct per_vhost_data__gs *vhd,
223 const lwsgw_hash *sid, char *username, int len)
224 {
225 struct lla lla = { username, len, 1 };
226 char s[150], esc[96];
227
228 lwsgw_expire_old_sessions(vhd);
229
230 lws_snprintf(s, sizeof(s) - 1,
231 "select username from sessions where name = '%s';",
232 lws_sql_purify(esc, sid->id, sizeof(esc) - 1));
233
234 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback, &lla, NULL) != SQLITE_OK) {
235 lwsl_err("Unable to create user table: %s\n",
236 sqlite3_errmsg(vhd->pdb));
237
238 return 1;
239 }
240
241 /* 0 if found */
242 return lla.results;
243 }
244
245 int
lwsgs_lookup_callback_user(void * priv,int cols,char ** col_val,char ** col_name)246 lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name)
247 {
248 struct lwsgs_user *u = (struct lwsgs_user *)priv;
249 int n;
250
251 for (n = 0; n < cols; n++) {
252 if (!strcmp(col_name[n], "username")) {
253 lws_strncpy(u->username, col_val[n], sizeof(u->username));
254 continue;
255 }
256 if (!strcmp(col_name[n], "ip")) {
257 lws_strncpy(u->ip, col_val[n], sizeof(u->ip));
258 continue;
259 }
260 if (!strcmp(col_name[n], "creation_time")) {
261 u->created = atol(col_val[n]);
262 continue;
263 }
264 if (!strcmp(col_name[n], "last_forgot_validated")) {
265 if (col_val[n])
266 u->last_forgot_validated = atol(col_val[n]);
267 else
268 u->last_forgot_validated = 0;
269 continue;
270 }
271 if (!strcmp(col_name[n], "email")) {
272 lws_strncpy(u->email, col_val[n], sizeof(u->email));
273 continue;
274 }
275 if (!strcmp(col_name[n], "verified")) {
276 u->verified = atoi(col_val[n]);
277 continue;
278 }
279 if (!strcmp(col_name[n], "pwhash")) {
280 lws_strncpy(u->pwhash.id, col_val[n], sizeof(u->pwhash.id));
281 continue;
282 }
283 if (!strcmp(col_name[n], "pwsalt")) {
284 lws_strncpy(u->pwsalt.id, col_val[n], sizeof(u->pwsalt.id));
285 continue;
286 }
287 if (!strcmp(col_name[n], "token")) {
288 lws_strncpy(u->token.id, col_val[n], sizeof(u->token.id));
289 continue;
290 }
291 }
292 return 0;
293 }
294
295 int
lwsgs_lookup_user(struct per_vhost_data__gs * vhd,const char * username,struct lwsgs_user * u)296 lwsgs_lookup_user(struct per_vhost_data__gs *vhd,
297 const char *username, struct lwsgs_user *u)
298 {
299 char s[150], esc[96];
300
301 u->username[0] = '\0';
302 lws_snprintf(s, sizeof(s) - 1,
303 "select username,creation_time,ip,email,verified,pwhash,pwsalt,last_forgot_validated "
304 "from users where username = '%s';",
305 lws_sql_purify(esc, username, sizeof(esc) - 1));
306
307 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, u, NULL) !=
308 SQLITE_OK) {
309 lwsl_err("Unable to lookup user: %s\n",
310 sqlite3_errmsg(vhd->pdb));
311
312 return -1;
313 }
314
315 return !u->username[0];
316 }
317
318 int
lwsgs_new_session_id(struct per_vhost_data__gs * vhd,lwsgw_hash * sid,const char * username,int exp)319 lwsgs_new_session_id(struct per_vhost_data__gs *vhd,
320 lwsgw_hash *sid, const char *username, int exp)
321 {
322 unsigned char sid_rand[32];
323 const char *u;
324 char s[300], esc[96], esc1[96];
325
326 if (username)
327 u = username;
328 else
329 u = "";
330
331 if (!sid) {
332 lwsl_err("%s: NULL sid\n", __func__);
333 return 1;
334 }
335
336 memset(sid, 0, sizeof(*sid));
337
338 if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
339 sizeof(sid_rand))
340 return 1;
341
342 sha256_to_lwsgw_hash(sid_rand, sid);
343
344 lws_snprintf(s, sizeof(s) - 1,
345 "insert into sessions(name, username, expire) "
346 "values ('%s', '%s', %u);",
347 lws_sql_purify(esc, sid->id, sizeof(esc) - 1),
348 lws_sql_purify(esc1, u, sizeof(esc1) - 1), exp);
349
350 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
351 lwsl_err("Unable to insert session: %s\n",
352 sqlite3_errmsg(vhd->pdb));
353
354 return 1;
355 }
356
357 lwsl_notice("%s: created session %s\n", __func__, sid->id);
358
359 return 0;
360 }
361
362 int
lwsgs_get_auth_level(struct per_vhost_data__gs * vhd,const char * username)363 lwsgs_get_auth_level(struct per_vhost_data__gs *vhd, const char *username)
364 {
365 struct lwsgs_user u;
366 int n = 0;
367
368 /* we are logged in as some kind of user */
369 if (username[0]) {
370 /* we are logged in as admin */
371 if (!strcmp(username, vhd->admin_user))
372 /* automatically verified */
373 n |= LWSGS_AUTH_VERIFIED | LWSGS_AUTH_ADMIN;
374 }
375
376 if (!lwsgs_lookup_user(vhd, username, &u)) {
377 if ((u.verified & 0xff) == LWSGS_VERIFIED_ACCEPTED)
378 n |= LWSGS_AUTH_LOGGED_IN | LWSGS_AUTH_VERIFIED;
379
380 if (u.last_forgot_validated > (time_t)lws_now_secs() - 300)
381 n |= LWSGS_AUTH_FORGOT_FLOW;
382 }
383
384 return n;
385 }
386
387 int
lwsgs_check_credentials(struct per_vhost_data__gs * vhd,const char * username,const char * password)388 lwsgs_check_credentials(struct per_vhost_data__gs *vhd,
389 const char *username, const char *password)
390 {
391 struct lws_genhash_ctx hash_ctx;
392 lwsgw_hash_bin hash_bin;
393 struct lwsgs_user u;
394 lwsgw_hash hash;
395
396 if (lwsgs_lookup_user(vhd, username, &u))
397 return -1;
398
399 lwsl_info("user %s found, salt '%s'\n", username, u.pwsalt.id);
400
401 /* sha256sum of password + salt */
402
403 if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256) ||
404 lws_genhash_update(&hash_ctx, password, strlen(password)) ||
405 lws_genhash_update(&hash_ctx, "-", 1) ||
406 lws_genhash_update(&hash_ctx, vhd->confounder, strlen(vhd->confounder)) ||
407 lws_genhash_update(&hash_ctx, "-", 1) ||
408 lws_genhash_update(&hash_ctx, u.pwsalt.id, strlen(u.pwsalt.id)) ||
409 lws_genhash_destroy(&hash_ctx, hash_bin.bin)) {
410 lws_genhash_destroy(&hash_ctx, NULL);
411
412 return 1;
413 }
414
415 sha256_to_lwsgw_hash(&hash_bin.bin[0], &hash);
416
417 return !!strcmp(hash.id, u.pwhash.id);
418 }
419
420 /* sets u->pwsalt and u->pwhash */
421
422 int
lwsgs_hash_password(struct per_vhost_data__gs * vhd,const char * password,struct lwsgs_user * u)423 lwsgs_hash_password(struct per_vhost_data__gs *vhd,
424 const char *password, struct lwsgs_user *u)
425 {
426 unsigned char sid_rand[32];
427 struct lws_genhash_ctx hash_ctx;
428 lwsgw_hash_bin hash_bin;
429
430 /* create a random salt as big as the hash */
431
432 if (lws_get_random(vhd->context, sid_rand,
433 sizeof(sid_rand)) !=
434 sizeof(sid_rand)) {
435 lwsl_err("Problem getting random for salt\n");
436 return 1;
437 }
438 sha256_to_lwsgw_hash(sid_rand, &u->pwsalt);
439 /*
440 if (lws_get_random(vhd->context, sid_rand,
441 sizeof(sid_rand)) !=
442 sizeof(sid_rand)) {
443 lwsl_err("Problem getting random for token\n");
444 return 1;
445 }
446 sha256_to_lwsgw_hash(sid_rand, &hash);
447 */
448 /* sha256sum of password + salt */
449
450 if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256) ||
451 lws_genhash_update(&hash_ctx, password, strlen(password)) ||
452 lws_genhash_update(&hash_ctx, "-", 1) ||
453 lws_genhash_update(&hash_ctx, vhd->confounder, strlen(vhd->confounder)) ||
454 lws_genhash_update(&hash_ctx, "-", 1) ||
455 lws_genhash_update(&hash_ctx, u->pwsalt.id, strlen(u->pwsalt.id)) ||
456 lws_genhash_destroy(&hash_ctx, hash_bin.bin)) {
457 lws_genhash_destroy(&hash_ctx, NULL);
458
459 return 1;
460 }
461
462 sha256_to_lwsgw_hash(&hash_bin.bin[0], &u->pwhash);
463
464 return 0;
465 }
466