• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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