fixed websockets

This commit is contained in:
Edward Emelianov 2020-10-27 16:40:38 +03:00
parent c1873f23ff
commit a7ff44b18a
7 changed files with 86 additions and 93 deletions

View File

@ -35,14 +35,14 @@ find_package(Threads REQUIRED)
###### pkgconfig ######
# pkg-config modules (for pkg-check-modules)
set(MODULES usefull_macros sqlite3)
set(MODULES usefull_macros sqlite3 onion)
# find packages:
find_package(PkgConfig REQUIRED)
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
###### additional flags ######
list(APPEND ${PROJ}_LIBRARIES "-lonion -lcrypt")
list(APPEND ${PROJ}_LIBRARIES "-lcrypt")
# change wrong behaviour with install prefix
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")

View File

@ -1,2 +1,4 @@
# onionserver
Simple web-server based on libonion
default admin: toor, password: p@ssw0rd

34
auth.c
View File

@ -149,6 +149,7 @@ sessinfo *qookieSession(onion_request *req){
}
onion_connection_status auth(_U_ onion_handler *h, onion_request *req, onion_response *res){
int retcode = OCS_CLOSE_CONNECTION;
if(!req || !res) return OCS_CLOSE_CONNECTION;
if(onion_request_get_flags(req) & OR_HEAD) {
onion_response_write_headers(res);
@ -195,33 +196,27 @@ onion_connection_status auth(_U_ onion_handler *h, onion_request *req, onion_res
userinfo *U = getUserData(username);
if(!U){
WARNX("User %s not found", username);
return OCS_FORBIDDEN;
retcode = OCS_FORBIDDEN;
goto closeconn;
}
char *pass = strdup(crypt(passwd, "$6$"));
if(!pass){
WARN("Error in ctypt or strdup");
freeUserInfo(&U);
return OCS_FORBIDDEN;
retcode = OCS_FORBIDDEN;
goto closeconn;
}
int comp = strcmp(pass, U->password);
freeUserInfo(&U);
FREE(pass);
if(comp){
WARNX("User %s give wrong password", username);
return OCS_FORBIDDEN;
retcode = OCS_FORBIDDEN;
goto closeconn;
}
session = MALLOC(sessinfo, 1);
session->atime = time(NULL);
session->username = strdup(username);
/*
onion_dict *data = onion_dict_new();
onion_dict_add(data, "UA", UA, 0);
onion_dict_add(data, "IP", host, 0);
onion_block *bl = onion_dict_to_json(data);
if(bl){
const char *json = onion_block_data(bl);
if(json) session->data = strdup(json);
}*/
session->data = strdup(json);
do{
FREE(session->sessID);
@ -238,7 +233,7 @@ onion_connection_status auth(_U_ onion_handler *h, onion_request *req, onion_res
onion_response_write0(res, AUTH_ANS_AUTHOK);
closeconn:
freeSessInfo(&session);
return OCS_CLOSE_CONNECTION;
return retcode;
}
/**
@ -552,22 +547,21 @@ int addSession(sessinfo *s, int modify){
if(!s) return 1;
sessinfo *byID = getSession(s->sessID);
sessinfo *bysID = getSession(s->sockID);
int errfound = 0;
if(byID || bysID){ // found session with same IDs
if(modify){
if(bysID && strcmp(bysID->sessID, s->sessID)){
freeSessInfo(&byID);
freeSessInfo(&bysID);
WARNX("Found another session with the same sockID");
return 2;
errfound = 2;
}
}else{
if(byID) WARNX("Found another session with the same sessID");
if(bysID) WARNX("Found another session with the same sockID");
errfound = 3;
}
}
freeSessInfo(&byID);
freeSessInfo(&bysID);
return 3;
}
}
if(errfound) return errfound;
pthread_mutex_lock(&sessionDB->mutex);
// 1-sessID, 2-sockID, 3-atime, 4-username, 5-data
sqlite3_reset(sessionDB->add);

2
auth.h
View File

@ -29,6 +29,8 @@
#define AUTH_ANS_AUTHOK "AuthOK"
#define AUTH_ANS_LOGOUT "LogOut"
#define AUTH_ANS_NOPASSWD "NoPassword"
#define AUTH_ANS_NOUSERDATA "NoUserData"
#define AUTH_ANS_WRONGIP "WrongIPUA"
typedef struct{
char *username; // user name

84
main.c
View File

@ -56,77 +56,64 @@ onion_connection_status get(_U_ onion_handler *h, onion_request *req, onion_resp
return OCS_CLOSE_CONNECTION;
}
static onion *os = NULL, *ow = NULL;
static onion *os = NULL;//, *ow = NULL;
static int STOP = 0;
void signals(int signo){
signal(signo, SIG_IGN);
STOP = 1;
if(os){
onion_free(os);
}
/*if(ow){
onion_free(ow);
}*/
closeSQLite();
if(os) onion_free(os);
if(ow) onion_free(ow);
sleep(1);
exit(signo);
}
// POST/GET server
static void *runPostGet(_U_ void *data){
os = onion_new(O_THREADED);
FNAME();
if(STOP) return NULL;
os = onion_new(O_POOL);
if(!(onion_flags(os) & O_SSL_AVAILABLE)){
ONION_ERROR("SSL support is not available");
signals(1);
}
int error = onion_set_certificate(os, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
if(error){
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
ONION_ERROR("Can't set certificate and key files (%s, %s)", G.certfile, G.keyfile);
signals(1);
}
DBG("PostGet @ port %s", G.port);
onion_set_port(os, G.port);
onion_url *url = onion_root_url(os);
onion_url_add_handler(url, "^static/", onion_handler_export_local_new("static"));
onion_url_add_with_data(url, "", onion_shortcut_internal_redirect, "static/index.html", NULL);
onion_url_add(url, "^auth/", auth);
onion_url_add(url, "^get/", get);
onion_url_add(url, "ws", websocket_run);
error = onion_listen(os);
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
if(error) ONION_ERROR("Can't create POST/GET server: %s", strerror(errno));
onion_free(os);
return NULL;
}
// Websocket server
static void *runWS(_U_ void *data){
ow = onion_new(O_THREADED);
if(!(onion_flags(ow) & O_SSL_AVAILABLE)){
ONION_ERROR("SSL support is not available");
signals(1);
}
int error = onion_set_certificate(ow, O_SSL_CERTIFICATE_KEY, G.certfile, G.keyfile);
if(error){
ONION_ERROR("Cant set certificate and key files (%s, %s)", G.certfile, G.keyfile);
signals(1);
}
onion_set_port(ow, G.wsport);
onion_url *url = onion_root_url(ow);
onion_url_add(url, "", websocket_run);
DBG("Listen websocket");
error = onion_listen(ow);
if(error) ONION_ERROR("Cant create POST/GET server: %s", strerror(errno));
onion_free(ow);
return NULL;
}
static void runServer(){
// if(G.logfilename) Cl_createlog();
signal(SIGTERM, signals);
signal(SIGINT, signals);
signal(SIGQUIT, signals);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP, SIG_IGN);
pthread_t pg_thread, ws_thread;
// if(G.logfilename) Cl_createlog();
pthread_t pg_thread;//, ws_thread;
if(pthread_create(&pg_thread, NULL, runPostGet, NULL)){
ERR("pthread_create()");
}
if(pthread_create(&ws_thread, NULL, runWS, NULL)){
ERR("pthread_create()");
}
do{
if(pthread_kill(pg_thread, 0) == ESRCH){ // POST/GET died
if(STOP) return;
if(pthread_kill(pg_thread, 0) == ESRCH){ // server died
WARNX("POST/GET server thread died");
putlog("POST/GET server thread died");
pthread_join(pg_thread, NULL);
@ -135,15 +122,6 @@ static void runServer(){
ERR("pthread_create()");
}
}
if((pthread_kill(pg_thread, 0) == ESRCH) || (pthread_kill(ws_thread, 0) == ESRCH)){ // died
WARNX("Websocket server thread died");
putlog("Websocket server thread died");
pthread_join(ws_thread, NULL);
if(pthread_create(&ws_thread, NULL, runWS, NULL)){
putlog("pthread_create() failed");
ERR("pthread_create()");
}
}
#if 0
usleep(1000); // sleep a little or thread's won't be able to lock mutex
if(dtime() - tgot < T_INTERVAL) continue;
@ -231,24 +209,6 @@ int main(int argc, char **argv){
}
}
if(G.useradd) adduser();
/*
sessinfo *x = MALLOC(sessinfo, 1);
x->data = strdup("some data");
x->atime = time(NULL);
x->username = strdup("luser");
do{
FREE(x->sessID);
FREE(x->sockID);
x->sessID = onion_sessions_generate_id();
x->sockID = onion_sessions_generate_id();
if(!addSession(x)){
green("Insert session: ID=%s, sockid=%s, atime=%lld, user=%s, data=%s\n",
x->sessID, x->sockID, x->atime, x->username, x->data);
break;
}
}while(1);
freeSessInfo(&x);
*/
if(G.dumpSessDB) showAllSessions();
if(G.delsession){
if(!deleteSession(G.delsession))

View File

@ -52,6 +52,7 @@ auth = function(){
}
function logout1(){
sendrequest("auth/?LogOut=1", _ilogin);
ws.close();
}
function sendlogpass(){
$("shadow").style.display = "none";
@ -67,7 +68,7 @@ auth = function(){
var ws;
function wsinit(){
delete(ws);
ws = new WebSocket('wss://localhost:8081');
ws = new WebSocket('wss://localhost:8080/ws');
ws.onopen = function(){ws.send("Akey="+wsKey);}; // send key after init
ws.onclose = function(evt){
var text = "WebSocket closed: ";

View File

@ -20,6 +20,7 @@
#include "websockets.h"
#include <errno.h>
#include <onion/dict.h>
#include <onion/log.h>
#include <stdio.h>
#include <string.h>
@ -29,12 +30,26 @@
// bit-fields of `data` field (websocket_cont)
#define WS_FLAG_NOTAUTHORIZED 1
typedef struct{
uint64_t UAhash;
uint64_t IPhash;
uint64_t flags;
} WSdata;
TODO: add logout!
// https://stackoverflow.com/a/57960443/1965803
static uint64_t MurmurOAAT64(const char *key){
uint64_t h = 525201411107845655ull;
for (;*key;++key){
h ^= (uint64_t)*key;
h *= 0x5bd1e9955bd1e995;
h ^= h >> 47;
}
return h;
}
static onion_connection_status websocket_cont(void *data, onion_websocket *ws, ssize_t dlen){
FNAME();
uint32_t flags = *((uint32_t*)data);
WSdata *wsdata = (WSdata*)data;
char tmp[BUFLEN+1];
if(dlen > BUFLEN) dlen = BUFLEN;
@ -46,19 +61,36 @@ static onion_connection_status websocket_cont(void *data, onion_websocket *ws, s
tmp[len] = 0;
//ONION_INFO("Read from websocket: %s (len=%d)", tmp, len);
DBG("WS: got %s", tmp);
if(flags & WS_FLAG_NOTAUTHORIZED){ // not authorized over websocket
if(wsdata->flags & WS_FLAG_NOTAUTHORIZED){ // not authorized over websocket
sessinfo *session = NULL;
if(strncmp(tmp, "Akey=", 5) == 0){ // got authorized key - check it
char *key = tmp + 5;
session = getSession(key);
/* here we should make a proper check, but for now do simplest */
}
if(!session){
onion_websocket_printf(ws, AUTH_ANS_NEEDAUTH);
WARNX("Wrong websocket session ID");
return OCS_FORBIDDEN;
}
flags &= ~WS_FLAG_NOTAUTHORIZED; // clear non-authorized flag
//
onion_dict *json = onion_dict_from_json(session->data);
freeSessInfo(&session);
if(json){
uint64_t UAhash = MurmurOAAT64(onion_dict_get(json, "User-Agent"));
uint64_t IPhash = MurmurOAAT64(onion_dict_get(json, "User-IP"));
if(wsdata->IPhash != IPhash || wsdata->UAhash != UAhash){
onion_websocket_printf(ws, AUTH_ANS_WRONGIP);
WARNX("Websocket IP/UA are wrong");
return OCS_FORBIDDEN;
}
red("WSdata checked!\n");
onion_dict_free(json);
}else{
onion_websocket_printf(ws, AUTH_ANS_NOUSERDATA);
WARNX("No user IP and/or UA in database");
return OCS_FORBIDDEN;
}
wsdata->flags &= ~WS_FLAG_NOTAUTHORIZED; // clear non-authorized flag
return OCS_NEED_MORE_DATA;
}
char *eq = strchr(tmp, '=');
@ -75,7 +107,6 @@ onion_connection_status websocket_run(_U_ void *data, onion_request *req, onion_
FNAME();
onion_websocket *ws = onion_websocket_new(req, res);
if (!ws){
green("PROC\n");
DBG("Processed");
return OCS_PROCESSED;
}
@ -83,8 +114,11 @@ onion_connection_status websocket_run(_U_ void *data, onion_request *req, onion_
const char *host = onion_request_get_client_description(req);
const char *UA = onion_request_get_header(req, "User-Agent");
green("Got WS connection from %s (UA: %s)\n", host, UA);
uint32_t *flags = calloc(1, 4);
onion_websocket_set_userdata(ws, (void*)flags, free);
WSdata *wsdata = calloc(1, sizeof(WSdata));
wsdata->flags = WS_FLAG_NOTAUTHORIZED;
wsdata->IPhash = MurmurOAAT64(host);
wsdata->UAhash = MurmurOAAT64(UA);
onion_websocket_set_userdata(ws, (void*)wsdata, free);
onion_websocket_set_callback(ws, websocket_cont);
return OCS_WEBSOCKET;
}