|
|
|
@ -21,6 +21,7 @@
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#include <netdb.h>
|
|
|
|
@ -63,6 +64,46 @@ void fatal(char *msg)
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2022-05-18 19:51:26,810 [INFO] websocket 0: 71.62.44.0 172.12.15.5 - "GET /api/get_frame_stats?client=auto HTTP/1.1" 403 2
|
|
|
|
|
static void weblog(const unsigned code, const unsigned websocket,
|
|
|
|
|
const uint8_t debug,
|
|
|
|
|
const char *origip, const char *ip,
|
|
|
|
|
const char *user, const uint8_t get,
|
|
|
|
|
const char *url, const uint64_t len)
|
|
|
|
|
{
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
struct tm local;
|
|
|
|
|
localtime_r(&tv.tv_sec, &local);
|
|
|
|
|
|
|
|
|
|
char timebuf[128];
|
|
|
|
|
strftime(timebuf, sizeof(timebuf), DATELOGFMT, &local);
|
|
|
|
|
|
|
|
|
|
const unsigned msec = tv.tv_usec / 1000;
|
|
|
|
|
const char *levelname = debug ? "DEBUG" : "INFO";
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, " %s,%03u [%s] websocket %u: %s %s %s \"%s %s HTTP/1.1\" %u %lu\n",
|
|
|
|
|
timebuf, msec, levelname, websocket, origip, ip, user,
|
|
|
|
|
get ? "GET" : "POST", url, code, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void wslog(char *logbuf, const unsigned websocket, const uint8_t debug)
|
|
|
|
|
{
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
struct tm local;
|
|
|
|
|
localtime_r(&tv.tv_sec, &local);
|
|
|
|
|
|
|
|
|
|
char timebuf[128];
|
|
|
|
|
strftime(timebuf, sizeof(timebuf), DATELOGFMT, &local);
|
|
|
|
|
|
|
|
|
|
const unsigned msec = tv.tv_usec / 1000;
|
|
|
|
|
const char *levelname = debug ? "DEBUG" : "INFO";
|
|
|
|
|
|
|
|
|
|
sprintf(logbuf, " %s,%03u [%s] websocket %u: ",
|
|
|
|
|
timebuf, msec, levelname, websocket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* resolve host with also IP address parsing */
|
|
|
|
|
int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
|
|
|
|
{
|
|
|
|
@ -783,7 +824,9 @@ static uint8_t isValidIp(const char *str, const unsigned len) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[]) {
|
|
|
|
|
static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[],
|
|
|
|
|
const char * const user, const char * const ip,
|
|
|
|
|
const char * const origip) {
|
|
|
|
|
char buf[4096];
|
|
|
|
|
char enc[PATH_MAX * 3 + 1];
|
|
|
|
|
unsigned i;
|
|
|
|
@ -798,6 +841,7 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[
|
|
|
|
|
"Content-type: text/plain\r\n"
|
|
|
|
|
"\r\n", path);
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(301, wsthread_handler_id, 0, origip, ip, user, 1, path, strlen(buf));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -811,6 +855,7 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[
|
|
|
|
|
"Content-type: text/html\r\n"
|
|
|
|
|
"\r\n<html><title>Directory Listing</title><body><h2>%s</h2><hr><ul>", path);
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
unsigned totallen = strlen(buf);
|
|
|
|
|
|
|
|
|
|
struct dirent **names;
|
|
|
|
|
const unsigned num = scandir(fullpath, &names, NULL, alphasort);
|
|
|
|
@ -829,19 +874,23 @@ static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[
|
|
|
|
|
names[i]->d_name);
|
|
|
|
|
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
totallen += strlen(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sprintf(buf, "</ul></body></html>");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
totallen += strlen(buf);
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, path, totallen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void servefile(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
static void servefile(ws_ctx_t *ws_ctx, const char *in, const char * const user,
|
|
|
|
|
const char * const ip, const char * const origip) {
|
|
|
|
|
char buf[4096], path[4096], fullpath[4096];
|
|
|
|
|
|
|
|
|
|
//fprintf(stderr, "http servefile input '%s'\n", in);
|
|
|
|
|
|
|
|
|
|
if (strncmp(in, "GET ", 4)) {
|
|
|
|
|
wserr("non-GET request, rejecting\n");
|
|
|
|
|
handler_msg("non-GET request, rejecting\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
in += 4;
|
|
|
|
@ -849,7 +898,7 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
unsigned len = end - in;
|
|
|
|
|
|
|
|
|
|
if (len < 1 || len > 1024 || strstr(in, "../")) {
|
|
|
|
|
wserr("Request too long (%u) or attempted dir traversal attack, rejecting\n", len);
|
|
|
|
|
handler_msg("Request too long (%u) or attempted dir traversal attack, rejecting\n", len);
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -866,19 +915,19 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
|
|
|
|
|
percent_decode(path, buf, 1);
|
|
|
|
|
|
|
|
|
|
wserr("Requested file '%s'\n", buf);
|
|
|
|
|
handler_msg("Requested file '%s'\n", buf);
|
|
|
|
|
sprintf(fullpath, "%s/%s", settings.httpdir, buf);
|
|
|
|
|
|
|
|
|
|
DIR *dir = opendir(fullpath);
|
|
|
|
|
if (dir) {
|
|
|
|
|
closedir(dir);
|
|
|
|
|
dirlisting(ws_ctx, fullpath, buf);
|
|
|
|
|
dirlisting(ws_ctx, fullpath, buf, user, ip, origip);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FILE *f = fopen(fullpath, "r");
|
|
|
|
|
if (!f) {
|
|
|
|
|
wserr("file not found or insufficient permissions\n");
|
|
|
|
|
handler_msg("file not found or insufficient permissions\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -903,6 +952,8 @@ static void servefile(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, path, strlen(buf) + filesize);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
nope:
|
|
|
|
|
sprintf(buf, "HTTP/1.1 404 Not Found\r\n"
|
|
|
|
@ -912,6 +963,7 @@ nope:
|
|
|
|
|
"\r\n"
|
|
|
|
|
"404");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(404, wsthread_handler_id, 0, origip, ip, user, 1, path, strlen(buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t allUsersPresent(const struct kasmpasswd_t * const inset) {
|
|
|
|
@ -948,7 +1000,7 @@ notfound:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void send403(ws_ctx_t *ws_ctx) {
|
|
|
|
|
static void send403(ws_ctx_t *ws_ctx, const char * const origip, const char * const ip) {
|
|
|
|
|
const char response[] = "HTTP/1.1 403 Forbidden\r\n"
|
|
|
|
|
"Server: KasmVNC/4.0\r\n"
|
|
|
|
|
"Connection: close\r\n"
|
|
|
|
@ -956,9 +1008,11 @@ static void send403(ws_ctx_t *ws_ctx) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"403 Forbidden";
|
|
|
|
|
ws_send(ws_ctx, response, strlen(response));
|
|
|
|
|
weblog(403, wsthread_handler_id, 0, origip, ip, "-", 1, "-", strlen(response));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in, const char * const user,
|
|
|
|
|
const char * const ip, const char * const origip) {
|
|
|
|
|
char buf[4096], path[4096];
|
|
|
|
|
uint8_t ret = 0; // 0 = continue checking
|
|
|
|
|
|
|
|
|
@ -967,24 +1021,24 @@ static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
unsigned len = end - in;
|
|
|
|
|
|
|
|
|
|
if (len < 1 || len > 1024 || strstr(in, "../")) {
|
|
|
|
|
wserr("Request too long (%u) or attempted dir traversal attack, rejecting\n", len);
|
|
|
|
|
handler_msg("Request too long (%u) or attempted dir traversal attack, rejecting\n", len);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end = memchr(in, '?', len);
|
|
|
|
|
if (end) {
|
|
|
|
|
wserr("Attempted GET params in a POST request, rejecting\n");
|
|
|
|
|
handler_msg("Attempted GET params in a POST request, rejecting\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(path, in, len);
|
|
|
|
|
path[len] = '\0';
|
|
|
|
|
|
|
|
|
|
wserr("Requested owner POST api '%s'\n", path);
|
|
|
|
|
handler_msg("Requested owner POST api '%s'\n", path);
|
|
|
|
|
|
|
|
|
|
in = strstr(in, "\r\n\r\n");
|
|
|
|
|
if (!in) {
|
|
|
|
|
wserr("No content\n");
|
|
|
|
|
handler_msg("No content\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
in += 4;
|
|
|
|
@ -1030,6 +1084,7 @@ static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"200 OK");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 0, path, strlen(buf));
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/remove_user") {
|
|
|
|
@ -1067,6 +1122,7 @@ static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"200 OK");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 0, path, strlen(buf));
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/update_user") {
|
|
|
|
@ -1104,7 +1160,7 @@ static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
set->entries[s].password,
|
|
|
|
|
set->entries[s].read,
|
|
|
|
|
set->entries[s].write, set->entries[s].owner)) {
|
|
|
|
|
wserr("Invalid params to update_user\n");
|
|
|
|
|
handler_msg("Invalid params to update_user\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1120,6 +1176,7 @@ static uint8_t ownerapi_post(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"200 OK");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 0, path, strlen(buf));
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
@ -1135,18 +1192,20 @@ nope:
|
|
|
|
|
"\r\n"
|
|
|
|
|
"400 Bad Request");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(400, wsthread_handler_id, 0, origip, ip, user, 0, path, strlen(buf));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
char buf[4096], path[4096], args[4096] = "";
|
|
|
|
|
static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in, const char * const user,
|
|
|
|
|
const char * const ip, const char * const origip) {
|
|
|
|
|
char buf[4096], path[4096], args[4096] = "", origpath[4096];
|
|
|
|
|
uint8_t ret = 0; // 0 = continue checking
|
|
|
|
|
|
|
|
|
|
if (strncmp(in, "GET ", 4)) {
|
|
|
|
|
if (!strncmp(in, "POST ", 5))
|
|
|
|
|
return ownerapi_post(ws_ctx, in);
|
|
|
|
|
return ownerapi_post(ws_ctx, in, user, ip, origip);
|
|
|
|
|
|
|
|
|
|
wserr("non-GET, non-POST request, rejecting\n");
|
|
|
|
|
handler_msg("non-GET, non-POST request, rejecting\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
in += 4;
|
|
|
|
@ -1154,10 +1213,13 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
unsigned len = end - in;
|
|
|
|
|
|
|
|
|
|
if (len < 1 || len > 1024 || strstr(in, "../")) {
|
|
|
|
|
wserr("Request too long (%u) or attempted dir traversal attack, rejecting\n", len);
|
|
|
|
|
handler_msg("Request too long (%u) or attempted dir traversal attack, rejecting\n", len);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(origpath, in, len);
|
|
|
|
|
origpath[len] = '\0';
|
|
|
|
|
|
|
|
|
|
end = memchr(in, '?', len);
|
|
|
|
|
if (end) {
|
|
|
|
|
len = end - in;
|
|
|
|
@ -1172,9 +1234,9 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
path[len] = '\0';
|
|
|
|
|
|
|
|
|
|
if (strstr(args, "password=")) {
|
|
|
|
|
wserr("Requested owner api '%s' with args (skipped, includes password)\n", path);
|
|
|
|
|
handler_msg("Requested owner api '%s' with args (skipped, includes password)\n", path);
|
|
|
|
|
} else {
|
|
|
|
|
wserr("Requested owner api '%s' with args '%s'\n", path, args);
|
|
|
|
|
handler_msg("Requested owner api '%s' with args '%s'\n", path, args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define entry(x) if (!strcmp(path, x))
|
|
|
|
@ -1216,8 +1278,9 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n", len);
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
ws_send(ws_ctx, staging, len);
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf) + len);
|
|
|
|
|
|
|
|
|
|
wserr("Screenshot hadn't changed and dedup was requested, sent hash\n");
|
|
|
|
|
handler_msg("Screenshot hadn't changed and dedup was requested, sent hash\n");
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else if (len) {
|
|
|
|
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
|
|
|
@ -1228,15 +1291,16 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n", len);
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
ws_send(ws_ctx, staging, len);
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf) + len);
|
|
|
|
|
|
|
|
|
|
wserr("Sent screenshot %u bytes\n", len);
|
|
|
|
|
handler_msg("Sent screenshot %u bytes\n", len);
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(staging);
|
|
|
|
|
|
|
|
|
|
if (!len) {
|
|
|
|
|
wserr("Invalid params to screenshot\n");
|
|
|
|
|
handler_msg("Invalid params to screenshot\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
} else entry("/api/create_user") {
|
|
|
|
@ -1285,7 +1349,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
goto nope;
|
|
|
|
|
|
|
|
|
|
if (!settings.adduserCb(settings.messager, decname, decpw, read, write, owner)) {
|
|
|
|
|
wserr("Invalid params to create_user\n");
|
|
|
|
|
handler_msg("Invalid params to create_user\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1297,6 +1361,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"200 OK");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf));
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/remove_user") {
|
|
|
|
@ -1313,7 +1378,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
goto nope;
|
|
|
|
|
|
|
|
|
|
if (!settings.removeCb(settings.messager, decname)) {
|
|
|
|
|
wserr("Invalid params to remove_user\n");
|
|
|
|
|
handler_msg("Invalid params to remove_user\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1325,6 +1390,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"200 OK");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf));
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/update_user") {
|
|
|
|
@ -1367,7 +1433,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
|
|
|
|
|
if (!settings.updateUserCb(settings.messager, decname, mask, "",
|
|
|
|
|
myread, mywrite, myowner)) {
|
|
|
|
|
wserr("Invalid params to update_user\n");
|
|
|
|
|
handler_msg("Invalid params to update_user\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1379,6 +1445,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"200 OK");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf));
|
|
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/get_bottleneck_stats") {
|
|
|
|
@ -1393,8 +1460,9 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n", strlen(statbuf));
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
ws_send(ws_ctx, statbuf, strlen(statbuf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf) + strlen(statbuf));
|
|
|
|
|
|
|
|
|
|
wserr("Sent bottleneck stats to API caller\n");
|
|
|
|
|
handler_msg("Sent bottleneck stats to API caller\n");
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/get_users") {
|
|
|
|
|
const char *ptr;
|
|
|
|
@ -1408,10 +1476,11 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n", strlen(ptr));
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
ws_send(ws_ctx, ptr, strlen(ptr));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf) + strlen(ptr));
|
|
|
|
|
|
|
|
|
|
free((char *) ptr);
|
|
|
|
|
|
|
|
|
|
wserr("Sent user list to API caller\n");
|
|
|
|
|
handler_msg("Sent user list to API caller\n");
|
|
|
|
|
ret = 1;
|
|
|
|
|
} else entry("/api/get_frame_stats") {
|
|
|
|
|
char statbuf[4096], decname[1024];
|
|
|
|
@ -1423,7 +1492,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
percent_decode(buf, decname, 0);
|
|
|
|
|
} else {
|
|
|
|
|
wserr("client param required\n");
|
|
|
|
|
handler_msg("client param required\n");
|
|
|
|
|
goto nope;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1465,7 +1534,7 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (failed) {
|
|
|
|
|
wserr("Main thread didn't respond, aborting (bug!)\n");
|
|
|
|
|
handler_msg("Main thread didn't respond, aborting (bug, if nothing happened for 10s)\n");
|
|
|
|
|
settings.resetFrameStatsCb(settings.messager);
|
|
|
|
|
goto timeout;
|
|
|
|
|
}
|
|
|
|
@ -1489,8 +1558,9 @@ static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
|
|
|
|
"\r\n", strlen(statbuf));
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
ws_send(ws_ctx, statbuf, strlen(statbuf));
|
|
|
|
|
weblog(200, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf) + strlen(statbuf));
|
|
|
|
|
|
|
|
|
|
wserr("Sent frame stats to API caller\n");
|
|
|
|
|
handler_msg("Sent frame stats to API caller\n");
|
|
|
|
|
ret = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1506,6 +1576,7 @@ nope:
|
|
|
|
|
"\r\n"
|
|
|
|
|
"400 Bad Request");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(400, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf));
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
timeout:
|
|
|
|
@ -1516,6 +1587,7 @@ timeout:
|
|
|
|
|
"\r\n"
|
|
|
|
|
"503 Service Unavailable");
|
|
|
|
|
ws_send(ws_ctx, buf, strlen(buf));
|
|
|
|
|
weblog(503, wsthread_handler_id, 0, origip, ip, user, 1, origpath, strlen(buf));
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1588,6 +1660,8 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Proxied?
|
|
|
|
|
char origip[64];
|
|
|
|
|
memcpy(origip, ip, 64);
|
|
|
|
|
const char *fwd = strcasestr(handshake, "X-Forwarded-For: ");
|
|
|
|
|
if (fwd) {
|
|
|
|
|
fwd += 17;
|
|
|
|
@ -1606,25 +1680,40 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Early URL parsing for the auth denies
|
|
|
|
|
char url[2048] = "-";
|
|
|
|
|
const char *end = strchr(handshake + 4, ' ');
|
|
|
|
|
if (end) {
|
|
|
|
|
len = end - (handshake + 4);
|
|
|
|
|
if (len > 1024)
|
|
|
|
|
len = 1024;
|
|
|
|
|
|
|
|
|
|
memcpy(url, handshake + 4, len);
|
|
|
|
|
url[len] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bl_isBlacklisted(ip)) {
|
|
|
|
|
wserr("IP %s is blacklisted, dropping\n", ip);
|
|
|
|
|
sprintf(response, "HTTP/1.1 401 Forbidden\r\n"
|
|
|
|
|
"\r\n");
|
|
|
|
|
ws_send(ws_ctx, response, strlen(response));
|
|
|
|
|
weblog(401, wsthread_handler_id, 0, origip, ip, "-", 1, url, strlen(response));
|
|
|
|
|
free_ws_ctx(ws_ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned char owner = 0;
|
|
|
|
|
char inuser[32] = "-";
|
|
|
|
|
if (!settings.disablebasicauth) {
|
|
|
|
|
const char *hdr = strstr(handshake, "Authorization: Basic ");
|
|
|
|
|
if (!hdr) {
|
|
|
|
|
bl_addFailure(ip);
|
|
|
|
|
handler_emsg("BasicAuth required, but client didn't send any. 401 Unauth\n");
|
|
|
|
|
wserr("Authentication attempt failed, BasicAuth required, but client didn't send any\n");
|
|
|
|
|
sprintf(response, "HTTP/1.1 401 Unauthorized\r\n"
|
|
|
|
|
"WWW-Authenticate: Basic realm=\"Websockify\"\r\n"
|
|
|
|
|
"\r\n");
|
|
|
|
|
ws_send(ws_ctx, response, strlen(response));
|
|
|
|
|
weblog(401, wsthread_handler_id, 0, origip, ip, "-", 1, url, strlen(response));
|
|
|
|
|
free_ws_ctx(ws_ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -1632,9 +1721,9 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
hdr += sizeof("Authorization: Basic ") - 1;
|
|
|
|
|
const char *end = strchr(hdr, '\r');
|
|
|
|
|
if (!end || end - hdr > 256) {
|
|
|
|
|
handler_emsg("Client sent invalid BasicAuth, 403 forbidden\n");
|
|
|
|
|
wserr("Authentication attempt failed, client sent invalid BasicAuth\n");
|
|
|
|
|
bl_addFailure(ip);
|
|
|
|
|
send403(ws_ctx);
|
|
|
|
|
send403(ws_ctx, origip, ip);
|
|
|
|
|
free_ws_ctx(ws_ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -1655,12 +1744,10 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
char pwbuf[4096];
|
|
|
|
|
struct kasmpasswd_t *set = readkasmpasswd(settings.passwdfile);
|
|
|
|
|
if (!set->num) {
|
|
|
|
|
fprintf(stderr, " websocket %d: Error: BasicAuth configured to read password from file %s, but the file doesn't exist or has no valid users\n",
|
|
|
|
|
wsthread_handler_id,
|
|
|
|
|
wserr("Error: BasicAuth configured to read password from file %s, but the file doesn't exist or has no valid users\n",
|
|
|
|
|
settings.passwdfile);
|
|
|
|
|
} else {
|
|
|
|
|
unsigned i;
|
|
|
|
|
char inuser[32];
|
|
|
|
|
unsigned char found = 0;
|
|
|
|
|
memcpy(inuser, response, resppw - response - 1);
|
|
|
|
|
inuser[resppw - response - 1] = '\0';
|
|
|
|
@ -1680,7 +1767,7 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
|
handler_emsg("BasicAuth user %s not found\n", inuser);
|
|
|
|
|
wserr("Authentication attempt failed, user %s does not exist\n", inuser);
|
|
|
|
|
}
|
|
|
|
|
free(set->entries);
|
|
|
|
|
free(set);
|
|
|
|
@ -1703,11 +1790,12 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len <= 0 || strcmp(authbuf, response)) {
|
|
|
|
|
handler_emsg("BasicAuth user/pw did not match\n");
|
|
|
|
|
wserr("Authentication attempt failed, wrong password for user %s\n", inuser);
|
|
|
|
|
bl_addFailure(ip);
|
|
|
|
|
sprintf(response, "HTTP/1.1 401 Forbidden\r\n"
|
|
|
|
|
"\r\n");
|
|
|
|
|
ws_send(ws_ctx, response, strlen(response));
|
|
|
|
|
weblog(401, wsthread_handler_id, 0, origip, ip, inuser, 1, url, strlen(response));
|
|
|
|
|
free_ws_ctx(ws_ctx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -1722,7 +1810,7 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
handler_emsg("HTTP request under /api/\n");
|
|
|
|
|
|
|
|
|
|
if (owner) {
|
|
|
|
|
if (ownerapi(ws_ctx, handshake))
|
|
|
|
|
if (ownerapi(ws_ctx, handshake, inuser, ip, origip))
|
|
|
|
|
goto done;
|
|
|
|
|
} else {
|
|
|
|
|
sprintf(response, "HTTP/1.1 401 Unauthorized\r\n"
|
|
|
|
@ -1732,12 +1820,13 @@ ws_ctx_t *do_handshake(int sock, char * const ip) {
|
|
|
|
|
"\r\n"
|
|
|
|
|
"401 Unauthorized");
|
|
|
|
|
ws_send(ws_ctx, response, strlen(response));
|
|
|
|
|
weblog(401, wsthread_handler_id, 0, origip, ip, inuser, 1, url, strlen(response));
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (settings.httpdir && settings.httpdir[0])
|
|
|
|
|
servefile(ws_ctx, handshake);
|
|
|
|
|
servefile(ws_ctx, handshake, inuser, ip, origip);
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
free_ws_ctx(ws_ctx);
|
|
|
|
@ -1843,9 +1932,12 @@ void *start_server(void *unused) {
|
|
|
|
|
}
|
|
|
|
|
struct wspass_t *pass = calloc(1, sizeof(struct wspass_t));
|
|
|
|
|
inet_ntop(cli_addr.sin_family, &cli_addr.sin_addr, pass->ip, sizeof(pass->ip));
|
|
|
|
|
fprintf(stderr, " websocket %d: got client connection from %s\n",
|
|
|
|
|
settings.handler_id,
|
|
|
|
|
|
|
|
|
|
char logbuf[2][1024];
|
|
|
|
|
wslog(logbuf[0], settings.handler_id, 0);
|
|
|
|
|
sprintf(logbuf[1], "got client connection from %s\n",
|
|
|
|
|
pass->ip);
|
|
|
|
|
fprintf(stderr, "%s%s", logbuf[0], logbuf[1]);
|
|
|
|
|
|
|
|
|
|
pthread_t tid;
|
|
|
|
|
pass->id = settings.handler_id;
|
|
|
|
|