Merge pull request #33 from kasmtech/vncserver-remove-basicauth
Vncserver remove basicauth
This commit is contained in:
@@ -2,6 +2,7 @@ FROM centos:centos7
|
|||||||
|
|
||||||
RUN yum install -y xterm
|
RUN yum install -y xterm
|
||||||
RUN yum install -y vim less
|
RUN yum install -y vim less
|
||||||
|
RUN yum install -y redhat-lsb-core
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
||||||
@@ -9,10 +10,9 @@ RUN yum localinstall -y /tmp/*.rpm
|
|||||||
|
|
||||||
RUN useradd -m foo
|
RUN useradd -m foo
|
||||||
|
|
||||||
USER foo:kasmvnc
|
USER foo:kasmvnc-cert
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
||||||
chmod +x ~/.vnc/xstartup
|
chmod +x ~/.vnc/xstartup
|
||||||
RUN echo bar | kasmvncpasswd -f > $HOME/.kasmpasswd && chmod 0600 $HOME/.kasmpasswd
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ FROM fedora:33
|
|||||||
|
|
||||||
RUN dnf install -y xterm
|
RUN dnf install -y xterm
|
||||||
RUN dnf install -y vim less
|
RUN dnf install -y vim less
|
||||||
|
RUN yum install -y redhat-lsb-core
|
||||||
|
|
||||||
ARG KASMVNC_PACKAGE_DIR
|
ARG KASMVNC_PACKAGE_DIR
|
||||||
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
COPY $KASMVNC_PACKAGE_DIR/*.rpm /tmp
|
||||||
@@ -9,10 +10,9 @@ RUN dnf localinstall -y /tmp/*.rpm
|
|||||||
|
|
||||||
RUN useradd -m foo
|
RUN useradd -m foo
|
||||||
|
|
||||||
USER foo:kasmvnc
|
USER foo:kasmvnc-cert
|
||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
||||||
chmod +x ~/.vnc/xstartup
|
chmod +x ~/.vnc/xstartup
|
||||||
RUN echo bar | kasmvncpasswd -f > $HOME/.kasmpasswd && chmod 0600 $HOME/.kasmpasswd
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/pki/tls/private/kasmvnc.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ USER foo
|
|||||||
|
|
||||||
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
RUN mkdir ~/.vnc && echo '/usr/bin/xterm &' >> ~/.vnc/xstartup && \
|
||||||
chmod +x ~/.vnc/xstartup
|
chmod +x ~/.vnc/xstartup
|
||||||
RUN echo bar | kasmvncpasswd -f > $HOME/.kasmpasswd && chmod 0600 $HOME/.kasmpasswd
|
|
||||||
|
|
||||||
ENTRYPOINT bash -c "vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/ssl/certs/ssl-cert-snakeoil.pem -key /etc/ssl/private/ssl-cert-snakeoil.key -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
ENTRYPOINT bash -c "echo -e \"$VNC_PW\n$VNC_PW\n\" | kasmvncpasswd -w -u \"$VNC_USER\" && vncserver :1 -interface 0.0.0.0 && vncserver -kill :1 && vncserver :1 -depth 24 -geometry 1280x1050 -websocketPort 8443 -cert /etc/ssl/certs/ssl-cert-snakeoil.pem -key /etc/ssl/private/ssl-cert-snakeoil.key -sslOnly -FrameRate=24 -interface 0.0.0.0 -httpd /usr/share/kasmvnc/www && tail -f $HOME/.vnc/*.log "
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
include_directories(${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
include_directories(${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
||||||
|
|
||||||
set(NETWORK_SOURCES
|
set(NETWORK_SOURCES
|
||||||
|
GetAPIMessager.cxx
|
||||||
Socket.cxx
|
Socket.cxx
|
||||||
TcpSocket.cxx
|
TcpSocket.cxx
|
||||||
websocket.c
|
websocket.c
|
||||||
|
|||||||
75
common/network/GetAPI.h
Normal file
75
common/network/GetAPI.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/* Copyright (C) 2021 Kasm
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NETWORK_GET_API_H__
|
||||||
|
#define __NETWORK_GET_API_H__
|
||||||
|
|
||||||
|
#include <kasmpasswd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <rfb/PixelBuffer.h>
|
||||||
|
#include <rfb/PixelFormat.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
class GetAPIMessager {
|
||||||
|
public:
|
||||||
|
GetAPIMessager(const char *passwdfile_);
|
||||||
|
|
||||||
|
// from main thread
|
||||||
|
void mainUpdateScreen(rfb::PixelBuffer *pb);
|
||||||
|
|
||||||
|
// from network threads
|
||||||
|
uint8_t *netGetScreenshot(uint16_t w, uint16_t h,
|
||||||
|
const uint8_t q, const bool dedup,
|
||||||
|
uint32_t &len, uint8_t *staging);
|
||||||
|
uint8_t netAddUser(const char name[], const char pw[], const bool write);
|
||||||
|
uint8_t netRemoveUser(const char name[]);
|
||||||
|
uint8_t netGiveControlTo(const char name[]);
|
||||||
|
|
||||||
|
enum USER_ACTION {
|
||||||
|
//USER_ADD, - handled locally for interactivity
|
||||||
|
USER_REMOVE,
|
||||||
|
USER_GIVE_CONTROL,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct action_data {
|
||||||
|
enum USER_ACTION action;
|
||||||
|
kasmpasswd_entry_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
pthread_mutex_t userMutex;
|
||||||
|
std::vector<action_data> actionQueue;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char *passwdfile;
|
||||||
|
|
||||||
|
pthread_mutex_t screenMutex;
|
||||||
|
rfb::ManagedPixelBuffer screenPb;
|
||||||
|
uint16_t screenW, screenH;
|
||||||
|
uint64_t screenHash;
|
||||||
|
|
||||||
|
std::vector<uint8_t> cachedJpeg;
|
||||||
|
uint16_t cachedW, cachedH;
|
||||||
|
uint8_t cachedQ;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __NETWORK_GET_API_H__
|
||||||
288
common/network/GetAPIMessager.cxx
Normal file
288
common/network/GetAPIMessager.cxx
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
/* Copyright (C) 2021 Kasm
|
||||||
|
*
|
||||||
|
* This is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This software is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this software; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||||
|
* USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <network/GetAPI.h>
|
||||||
|
#include <rfb/ConnParams.h>
|
||||||
|
#include <rfb/LogWriter.h>
|
||||||
|
#include <rfb/JpegCompressor.h>
|
||||||
|
#include <rfb/xxhash.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
using namespace rfb;
|
||||||
|
|
||||||
|
static LogWriter vlog("GetAPIMessager");
|
||||||
|
|
||||||
|
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
|
||||||
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
|
const float tgtdiff);
|
||||||
|
|
||||||
|
struct TightJPEGConfiguration {
|
||||||
|
int quality;
|
||||||
|
int subsampling;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct TightJPEGConfiguration conf[10] = {
|
||||||
|
{ 15, subsample4X }, // 0
|
||||||
|
{ 29, subsample4X }, // 1
|
||||||
|
{ 41, subsample4X }, // 2
|
||||||
|
{ 42, subsample2X }, // 3
|
||||||
|
{ 62, subsample2X }, // 4
|
||||||
|
{ 77, subsample2X }, // 5
|
||||||
|
{ 79, subsampleNone }, // 6
|
||||||
|
{ 86, subsampleNone }, // 7
|
||||||
|
{ 92, subsampleNone }, // 8
|
||||||
|
{ 100, subsampleNone } // 9
|
||||||
|
};
|
||||||
|
|
||||||
|
GetAPIMessager::GetAPIMessager(const char *passwdfile_): passwdfile(passwdfile_),
|
||||||
|
screenW(0), screenH(0), screenHash(0),
|
||||||
|
cachedW(0), cachedH(0), cachedQ(0) {
|
||||||
|
|
||||||
|
pthread_mutex_init(&screenMutex, NULL);
|
||||||
|
pthread_mutex_init(&userMutex, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from main thread
|
||||||
|
void GetAPIMessager::mainUpdateScreen(rfb::PixelBuffer *pb) {
|
||||||
|
if (pthread_mutex_trylock(&screenMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int stride;
|
||||||
|
const rdr::U8 * const buf = pb->getBuffer(pb->getRect(), &stride);
|
||||||
|
|
||||||
|
if (pb->width() != screenW || pb->height() != screenH) {
|
||||||
|
screenHash = 0;
|
||||||
|
screenW = pb->width();
|
||||||
|
screenH = pb->height();
|
||||||
|
screenPb.setPF(pb->getPF());
|
||||||
|
screenPb.setSize(screenW, screenH);
|
||||||
|
|
||||||
|
cachedW = cachedH = cachedQ = 0;
|
||||||
|
cachedJpeg.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t newHash = XXH64(buf, pb->area() * 4, 0);
|
||||||
|
if (newHash != screenHash) {
|
||||||
|
cachedW = cachedH = cachedQ = 0;
|
||||||
|
cachedJpeg.clear();
|
||||||
|
|
||||||
|
screenHash = newHash;
|
||||||
|
rdr::U8 *rw = screenPb.getBufferRW(screenPb.getRect(), &stride);
|
||||||
|
memcpy(rw, buf, screenW * screenH * 4);
|
||||||
|
screenPb.commitBufferRW(screenPb.getRect());
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&screenMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from network threads
|
||||||
|
uint8_t *GetAPIMessager::netGetScreenshot(uint16_t w, uint16_t h,
|
||||||
|
const uint8_t q, const bool dedup,
|
||||||
|
uint32_t &len, uint8_t *staging) {
|
||||||
|
|
||||||
|
uint8_t *ret = NULL;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
if (w > screenW)
|
||||||
|
w = screenW;
|
||||||
|
if (h > screenH)
|
||||||
|
h = screenH;
|
||||||
|
|
||||||
|
if (!screenW || !screenH)
|
||||||
|
vlog.error("Screenshot requested but no screenshot exists (screen hasn't been viewed)");
|
||||||
|
|
||||||
|
if (!w || !h || q > 9 || !staging)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&screenMutex))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (w == cachedW && h == cachedH && q == cachedQ) {
|
||||||
|
if (dedup) {
|
||||||
|
// Return the hash of the unchanged image
|
||||||
|
sprintf((char *) staging, "%" PRIx64, screenHash);
|
||||||
|
ret = staging;
|
||||||
|
len = 16;
|
||||||
|
} else {
|
||||||
|
// Return the cached image
|
||||||
|
len = cachedJpeg.size();
|
||||||
|
ret = staging;
|
||||||
|
memcpy(ret, &cachedJpeg[0], len);
|
||||||
|
|
||||||
|
vlog.info("Returning cached screenshot");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Encode the new JPEG, cache it
|
||||||
|
JpegCompressor jc;
|
||||||
|
int quality, subsampling;
|
||||||
|
|
||||||
|
quality = conf[q].quality;
|
||||||
|
subsampling = conf[q].subsampling;
|
||||||
|
|
||||||
|
jc.clear();
|
||||||
|
int stride;
|
||||||
|
|
||||||
|
if (w != screenW || h != screenH) {
|
||||||
|
float xdiff = w / (float) screenW;
|
||||||
|
float ydiff = h / (float) screenH;
|
||||||
|
const float diff = xdiff < ydiff ? xdiff : ydiff;
|
||||||
|
|
||||||
|
const uint16_t neww = screenW * diff;
|
||||||
|
const uint16_t newh = screenH * diff;
|
||||||
|
|
||||||
|
const PixelBuffer *scaled = progressiveBilinearScale(&screenPb, neww, newh, diff);
|
||||||
|
const rdr::U8 * const buf = scaled->getBuffer(scaled->getRect(), &stride);
|
||||||
|
|
||||||
|
jc.compress(buf, stride, scaled->getRect(),
|
||||||
|
scaled->getPF(), quality, subsampling);
|
||||||
|
|
||||||
|
cachedJpeg.resize(jc.length());
|
||||||
|
memcpy(&cachedJpeg[0], jc.data(), jc.length());
|
||||||
|
|
||||||
|
delete scaled;
|
||||||
|
|
||||||
|
vlog.info("Returning scaled screenshot");
|
||||||
|
} else {
|
||||||
|
const rdr::U8 * const buf = screenPb.getBuffer(screenPb.getRect(), &stride);
|
||||||
|
|
||||||
|
jc.compress(buf, stride, screenPb.getRect(),
|
||||||
|
screenPb.getPF(), quality, subsampling);
|
||||||
|
|
||||||
|
cachedJpeg.resize(jc.length());
|
||||||
|
memcpy(&cachedJpeg[0], jc.data(), jc.length());
|
||||||
|
|
||||||
|
vlog.info("Returning normal screenshot");
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedQ = q;
|
||||||
|
cachedW = w;
|
||||||
|
cachedH = h;
|
||||||
|
|
||||||
|
len = cachedJpeg.size();
|
||||||
|
ret = staging;
|
||||||
|
memcpy(ret, &cachedJpeg[0], len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&screenMutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define USERNAME_LEN sizeof(((struct kasmpasswd_entry_t *)0)->user)
|
||||||
|
#define PASSWORD_LEN sizeof(((struct kasmpasswd_entry_t *)0)->password)
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netAddUser(const char name[], const char pw[], const bool write) {
|
||||||
|
if (strlen(name) >= USERNAME_LEN) {
|
||||||
|
vlog.error("Username too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(pw) >= PASSWORD_LEN) {
|
||||||
|
vlog.error("Password too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!passwdfile)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
action_data act;
|
||||||
|
|
||||||
|
memcpy(act.data.user, name, USERNAME_LEN);
|
||||||
|
act.data.user[USERNAME_LEN - 1] = '\0';
|
||||||
|
memcpy(act.data.password, pw, PASSWORD_LEN);
|
||||||
|
act.data.password[PASSWORD_LEN - 1] = '\0';
|
||||||
|
act.data.owner = 0;
|
||||||
|
act.data.write = write;
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// This needs to be handled locally for proper interactivity
|
||||||
|
// (consider adding users when nobody is connected).
|
||||||
|
// The mutex and atomic rename keep things in sync.
|
||||||
|
|
||||||
|
struct kasmpasswd_t *set = readkasmpasswd(passwdfile);
|
||||||
|
unsigned s;
|
||||||
|
for (s = 0; s < set->num; s++) {
|
||||||
|
if (!strcmp(set->entries[s].user, act.data.user)) {
|
||||||
|
vlog.error("Can't create user %s, already exists", act.data.user);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = set->num++;
|
||||||
|
set->entries = (struct kasmpasswd_entry_t *) realloc(set->entries,
|
||||||
|
set->num * sizeof(struct kasmpasswd_entry_t));
|
||||||
|
set->entries[s] = act.data;
|
||||||
|
|
||||||
|
writekasmpasswd(passwdfile, set);
|
||||||
|
vlog.info("User %s created", act.data.user);
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&userMutex);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netRemoveUser(const char name[]) {
|
||||||
|
if (strlen(name) >= USERNAME_LEN) {
|
||||||
|
vlog.error("Username too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
action_data act;
|
||||||
|
act.action = USER_REMOVE;
|
||||||
|
|
||||||
|
memcpy(act.data.user, name, USERNAME_LEN);
|
||||||
|
act.data.user[USERNAME_LEN - 1] = '\0';
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
actionQueue.push_back(act);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userMutex);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GetAPIMessager::netGiveControlTo(const char name[]) {
|
||||||
|
if (strlen(name) >= USERNAME_LEN) {
|
||||||
|
vlog.error("Username too long");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
action_data act;
|
||||||
|
act.action = USER_GIVE_CONTROL;
|
||||||
|
|
||||||
|
memcpy(act.data.user, name, USERNAME_LEN);
|
||||||
|
act.data.user[USERNAME_LEN - 1] = '\0';
|
||||||
|
|
||||||
|
if (pthread_mutex_lock(&userMutex))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
actionQueue.push_back(act);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&userMutex);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -74,6 +74,8 @@ namespace network {
|
|||||||
virtual ~ConnectionFilter() {}
|
virtual ~ConnectionFilter() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GetAPIMessager;
|
||||||
|
|
||||||
class SocketListener {
|
class SocketListener {
|
||||||
public:
|
public:
|
||||||
SocketListener(int fd);
|
SocketListener(int fd);
|
||||||
@@ -93,6 +95,8 @@ namespace network {
|
|||||||
void setFilter(ConnectionFilter* f) {filter = f;}
|
void setFilter(ConnectionFilter* f) {filter = f;}
|
||||||
int getFd() {return fd;}
|
int getFd() {return fd;}
|
||||||
|
|
||||||
|
virtual GetAPIMessager *getMessager() { return NULL; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SocketListener();
|
SocketListener();
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
|
#include <network/GetAPI.h>
|
||||||
#include <network/TcpSocket.h>
|
#include <network/TcpSocket.h>
|
||||||
#include <rfb/LogWriter.h>
|
#include <rfb/LogWriter.h>
|
||||||
#include <rfb/Configuration.h>
|
#include <rfb/Configuration.h>
|
||||||
@@ -431,10 +432,37 @@ int TcpListener::getMyPort() {
|
|||||||
|
|
||||||
extern settings_t settings;
|
extern settings_t settings;
|
||||||
|
|
||||||
|
static uint8_t *screenshotCb(void *messager, uint16_t w, uint16_t h, const uint8_t q,
|
||||||
|
const uint8_t dedup,
|
||||||
|
uint32_t *len, uint8_t *staging)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netGetScreenshot(w, h, q, dedup, *len, staging);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t adduserCb(void *messager, const char name[], const char pw[],
|
||||||
|
const uint8_t write)
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netAddUser(name, pw, write);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t removeCb(void *messager, const char name[])
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netRemoveUser(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t givecontrolCb(void *messager, const char name[])
|
||||||
|
{
|
||||||
|
GetAPIMessager *msgr = (GetAPIMessager *) messager;
|
||||||
|
return msgr->netGiveControlTo(name);
|
||||||
|
}
|
||||||
|
|
||||||
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
||||||
socklen_t listenaddrlen,
|
socklen_t listenaddrlen,
|
||||||
bool sslonly, const char *cert, const char *certkey,
|
bool sslonly, const char *cert, const char *certkey,
|
||||||
const char *basicauth,
|
bool disablebasicauth,
|
||||||
const char *httpdir)
|
const char *httpdir)
|
||||||
{
|
{
|
||||||
int one = 1;
|
int one = 1;
|
||||||
@@ -504,7 +532,7 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
settings.passwdfile = strdup(wexp.we_wordv[0]);
|
settings.passwdfile = strdup(wexp.we_wordv[0]);
|
||||||
wordfree(&wexp);
|
wordfree(&wexp);
|
||||||
|
|
||||||
settings.basicauth = basicauth;
|
settings.disablebasicauth = disablebasicauth;
|
||||||
settings.cert = cert;
|
settings.cert = cert;
|
||||||
settings.key = certkey;
|
settings.key = certkey;
|
||||||
settings.ssl_only = sslonly;
|
settings.ssl_only = sslonly;
|
||||||
@@ -515,6 +543,12 @@ WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr,
|
|||||||
|
|
||||||
settings.listen_sock = sock;
|
settings.listen_sock = sock;
|
||||||
|
|
||||||
|
settings.messager = messager = new GetAPIMessager(settings.passwdfile);
|
||||||
|
settings.screenshotCb = screenshotCb;
|
||||||
|
settings.adduserCb = adduserCb;
|
||||||
|
settings.removeCb = removeCb;
|
||||||
|
settings.givecontrolCb = givecontrolCb;
|
||||||
|
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
pthread_create(&tid, NULL, start_server, NULL);
|
pthread_create(&tid, NULL, start_server, NULL);
|
||||||
}
|
}
|
||||||
@@ -684,7 +718,7 @@ void network::createTcpListeners(std::list<SocketListener*> *listeners,
|
|||||||
void network::createWebsocketListeners(std::list<SocketListener*> *listeners,
|
void network::createWebsocketListeners(std::list<SocketListener*> *listeners,
|
||||||
const struct addrinfo *ai,
|
const struct addrinfo *ai,
|
||||||
bool sslonly, const char *cert, const char *certkey,
|
bool sslonly, const char *cert, const char *certkey,
|
||||||
const char *basicauth,
|
bool disablebasicauth,
|
||||||
const char *httpdir)
|
const char *httpdir)
|
||||||
{
|
{
|
||||||
const struct addrinfo *current;
|
const struct addrinfo *current;
|
||||||
@@ -711,7 +745,7 @@ void network::createWebsocketListeners(std::list<SocketListener*> *listeners,
|
|||||||
try {
|
try {
|
||||||
new_listeners.push_back(new WebsocketListener(current->ai_addr,
|
new_listeners.push_back(new WebsocketListener(current->ai_addr,
|
||||||
current->ai_addrlen,
|
current->ai_addrlen,
|
||||||
sslonly, cert, certkey, basicauth,
|
sslonly, cert, certkey, disablebasicauth,
|
||||||
httpdir));
|
httpdir));
|
||||||
} catch (SocketException& e) {
|
} catch (SocketException& e) {
|
||||||
// Ignore this if it is due to lack of address family support on
|
// Ignore this if it is due to lack of address family support on
|
||||||
@@ -740,7 +774,7 @@ void network::createWebsocketListeners(std::list<SocketListener*> *listeners,
|
|||||||
bool sslonly,
|
bool sslonly,
|
||||||
const char *cert,
|
const char *cert,
|
||||||
const char *certkey,
|
const char *certkey,
|
||||||
const char *basicauth,
|
bool disablebasicauth,
|
||||||
const char *httpdir)
|
const char *httpdir)
|
||||||
{
|
{
|
||||||
if (addr && !strcmp(addr, "local")) {
|
if (addr && !strcmp(addr, "local")) {
|
||||||
@@ -768,7 +802,7 @@ void network::createWebsocketListeners(std::list<SocketListener*> *listeners,
|
|||||||
ai[1].ai_addrlen = sizeof(sa[1].u.sin6);
|
ai[1].ai_addrlen = sizeof(sa[1].u.sin6);
|
||||||
ai[1].ai_next = NULL;
|
ai[1].ai_next = NULL;
|
||||||
|
|
||||||
createWebsocketListeners(listeners, ai, sslonly, cert, certkey, basicauth, httpdir);
|
createWebsocketListeners(listeners, ai, sslonly, cert, certkey, disablebasicauth, httpdir);
|
||||||
} else {
|
} else {
|
||||||
struct addrinfo *ai, hints;
|
struct addrinfo *ai, hints;
|
||||||
char service[16];
|
char service[16];
|
||||||
@@ -791,7 +825,7 @@ void network::createWebsocketListeners(std::list<SocketListener*> *listeners,
|
|||||||
gai_strerror(result));
|
gai_strerror(result));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createWebsocketListeners(listeners, ai, sslonly, cert, certkey, basicauth, httpdir);
|
createWebsocketListeners(listeners, ai, sslonly, cert, certkey, disablebasicauth, httpdir);
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
throw;
|
throw;
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace network {
|
|||||||
public:
|
public:
|
||||||
WebsocketListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen,
|
WebsocketListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen,
|
||||||
bool sslonly, const char *cert, const char *certkey,
|
bool sslonly, const char *cert, const char *certkey,
|
||||||
const char *basicauth,
|
bool disablebasicauth,
|
||||||
const char *httpdir);
|
const char *httpdir);
|
||||||
|
|
||||||
virtual int getMyPort();
|
virtual int getMyPort();
|
||||||
@@ -100,8 +100,12 @@ namespace network {
|
|||||||
|
|
||||||
int internalSocket;
|
int internalSocket;
|
||||||
|
|
||||||
|
virtual GetAPIMessager *getMessager() { return messager; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Socket* createSocket(int fd);
|
virtual Socket* createSocket(int fd);
|
||||||
|
private:
|
||||||
|
GetAPIMessager *messager;
|
||||||
};
|
};
|
||||||
|
|
||||||
void createLocalTcpListeners(std::list<SocketListener*> *listeners,
|
void createLocalTcpListeners(std::list<SocketListener*> *listeners,
|
||||||
@@ -112,7 +116,7 @@ namespace network {
|
|||||||
bool sslonly,
|
bool sslonly,
|
||||||
const char *cert,
|
const char *cert,
|
||||||
const char *certkey,
|
const char *certkey,
|
||||||
const char *basicauth,
|
bool disablebasicauth,
|
||||||
const char *httpdir);
|
const char *httpdir);
|
||||||
void createTcpListeners(std::list<SocketListener*> *listeners,
|
void createTcpListeners(std::list<SocketListener*> *listeners,
|
||||||
const char *addr,
|
const char *addr,
|
||||||
@@ -124,7 +128,7 @@ namespace network {
|
|||||||
bool sslonly,
|
bool sslonly,
|
||||||
const char *cert,
|
const char *cert,
|
||||||
const char *certkey,
|
const char *certkey,
|
||||||
const char *basicauth,
|
bool disablebasicauth,
|
||||||
const char *httpdir);
|
const char *httpdir);
|
||||||
|
|
||||||
typedef struct vnc_sockaddr {
|
typedef struct vnc_sockaddr {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <crypt.h>
|
#include <crypt.h>
|
||||||
@@ -83,6 +84,55 @@ int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *parse_get(const char * const in, const char * const opt, unsigned *len) {
|
||||||
|
const char *start = in;
|
||||||
|
const char *end = strchrnul(start, '&');
|
||||||
|
const unsigned optlen = strlen(opt);
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (!strncmp(start, opt, optlen)) {
|
||||||
|
const char *arg = strchr(start, '=');
|
||||||
|
if (!arg)
|
||||||
|
return "";
|
||||||
|
arg++;
|
||||||
|
*len = end - arg;
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
end++;
|
||||||
|
start = end;
|
||||||
|
end = strchrnul(start, '&');
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void percent_decode(const char *src, char *dst) {
|
||||||
|
while (1) {
|
||||||
|
if (!*src)
|
||||||
|
break;
|
||||||
|
if (*src != '%') {
|
||||||
|
*dst++ = *src++;
|
||||||
|
} else {
|
||||||
|
src++;
|
||||||
|
if (!src[0] || !src[1])
|
||||||
|
break;
|
||||||
|
char hex[3];
|
||||||
|
hex[0] = src[0];
|
||||||
|
hex[1] = src[1];
|
||||||
|
hex[2] = '\0';
|
||||||
|
|
||||||
|
src += 2;
|
||||||
|
*dst++ = strtol(hex, NULL, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSL Wrapper Code
|
* SSL Wrapper Code
|
||||||
@@ -814,6 +864,226 @@ nope:
|
|||||||
ws_send(ws_ctx, buf, strlen(buf));
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t ownerapi(ws_ctx_t *ws_ctx, const char *in) {
|
||||||
|
char buf[4096], path[4096], args[4096] = "";
|
||||||
|
uint8_t ret = 0; // 0 = continue checking
|
||||||
|
|
||||||
|
if (strncmp(in, "GET ", 4)) {
|
||||||
|
wserr("non-GET request, rejecting\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
in += 4;
|
||||||
|
const char *end = strchr(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);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = memchr(in, '?', len);
|
||||||
|
if (end) {
|
||||||
|
len = end - in;
|
||||||
|
end++;
|
||||||
|
|
||||||
|
const char *argend = strchr(end, ' ');
|
||||||
|
strncpy(args, end, argend - end);
|
||||||
|
args[argend - end] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(path, in, len);
|
||||||
|
path[len] = '\0';
|
||||||
|
|
||||||
|
if (strstr(args, "password=")) {
|
||||||
|
wserr("Requested owner api '%s' with args (skipped, includes password)\n", path);
|
||||||
|
} else {
|
||||||
|
wserr("Requested owner api '%s' with args '%s'\n", path, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define entry(x) if (!strcmp(path, x))
|
||||||
|
|
||||||
|
const char *param;
|
||||||
|
|
||||||
|
entry("/api/get_screenshot") {
|
||||||
|
uint8_t q = 7, dedup = 0;
|
||||||
|
uint16_t w = 4096, h = 4096;
|
||||||
|
|
||||||
|
param = parse_get(args, "width", &len);
|
||||||
|
if (len && isdigit(param[0]))
|
||||||
|
w = atoi(param);
|
||||||
|
|
||||||
|
param = parse_get(args, "height", &len);
|
||||||
|
if (len && isdigit(param[0]))
|
||||||
|
h = atoi(param);
|
||||||
|
|
||||||
|
param = parse_get(args, "quality", &len);
|
||||||
|
if (len && isdigit(param[0]))
|
||||||
|
q = atoi(param);
|
||||||
|
|
||||||
|
param = parse_get(args, "deduplicate", &len);
|
||||||
|
if (len && isalpha(param[0])) {
|
||||||
|
if (!strncmp(param, "true", len))
|
||||||
|
dedup = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *staging = malloc(1024 * 1024 * 8);
|
||||||
|
|
||||||
|
settings.screenshotCb(settings.messager, w, h, q, dedup, &len, staging);
|
||||||
|
|
||||||
|
if (len == 16) {
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: %u\r\n"
|
||||||
|
"\r\n", len);
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
ws_send(ws_ctx, staging, len);
|
||||||
|
|
||||||
|
wserr("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"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: image/jpeg\r\n"
|
||||||
|
"Content-length: %u\r\n"
|
||||||
|
"\r\n", len);
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
ws_send(ws_ctx, staging, len);
|
||||||
|
|
||||||
|
wserr("Sent screenshot %u bytes\n", len);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(staging);
|
||||||
|
|
||||||
|
if (!len) {
|
||||||
|
wserr("Invalid params to screenshot\n");
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
} else entry("/api/create_user") {
|
||||||
|
char decname[1024] = "", decpw[1024] = "";
|
||||||
|
uint8_t write = 0;
|
||||||
|
|
||||||
|
param = parse_get(args, "name", &len);
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, param, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
percent_decode(buf, decname);
|
||||||
|
}
|
||||||
|
|
||||||
|
param = parse_get(args, "password", &len);
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, param, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
percent_decode(buf, decpw);
|
||||||
|
|
||||||
|
struct crypt_data cdata;
|
||||||
|
cdata.initialized = 0;
|
||||||
|
|
||||||
|
const char *encrypted = crypt_r(decpw, "$5$kasm$", &cdata);
|
||||||
|
strcpy(decpw, encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
param = parse_get(args, "write", &len);
|
||||||
|
if (len && isalpha(param[0])) {
|
||||||
|
if (!strncmp(param, "true", len))
|
||||||
|
write = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decname[0] || !decpw[0])
|
||||||
|
goto nope;
|
||||||
|
|
||||||
|
if (!settings.adduserCb(settings.messager, decname, decpw, write)) {
|
||||||
|
wserr("Invalid params to create_user\n");
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: 6\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"200 OK");
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
|
||||||
|
ret = 1;
|
||||||
|
} else entry("/api/remove_user") {
|
||||||
|
char decname[1024] = "";
|
||||||
|
|
||||||
|
param = parse_get(args, "name", &len);
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, param, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
percent_decode(buf, decname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decname[0])
|
||||||
|
goto nope;
|
||||||
|
|
||||||
|
if (!settings.removeCb(settings.messager, decname)) {
|
||||||
|
wserr("Invalid params to remove_user\n");
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: 6\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"200 OK");
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
|
||||||
|
wserr("Passed remove_user request to main thread\n");
|
||||||
|
ret = 1;
|
||||||
|
} else entry("/api/give_control") {
|
||||||
|
char decname[1024] = "";
|
||||||
|
|
||||||
|
param = parse_get(args, "name", &len);
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, param, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
percent_decode(buf, decname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!decname[0])
|
||||||
|
goto nope;
|
||||||
|
|
||||||
|
if (!settings.givecontrolCb(settings.messager, decname)) {
|
||||||
|
wserr("Invalid params to give_control\n");
|
||||||
|
goto nope;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "HTTP/1.1 200 OK\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"Content-length: 6\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"200 OK");
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
|
||||||
|
wserr("Passed give_control request to main thread\n");
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef entry
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
nope:
|
||||||
|
sprintf(buf, "HTTP/1.1 400 Bad Request\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"400 Bad Request");
|
||||||
|
ws_send(ws_ctx, buf, strlen(buf));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
ws_ctx_t *do_handshake(int sock) {
|
ws_ctx_t *do_handshake(int sock) {
|
||||||
char handshake[4096], response[4096], sha1[29], trailer[17];
|
char handshake[4096], response[4096], sha1[29], trailer[17];
|
||||||
char *scheme, *pre;
|
char *scheme, *pre;
|
||||||
@@ -882,8 +1152,8 @@ ws_ctx_t *do_handshake(int sock) {
|
|||||||
usleep(10);
|
usleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *colon;
|
unsigned char owner = 0;
|
||||||
if ((colon = strchr(settings.basicauth, ':'))) {
|
if (!settings.disablebasicauth) {
|
||||||
const char *hdr = strstr(handshake, "Authorization: Basic ");
|
const char *hdr = strstr(handshake, "Authorization: Basic ");
|
||||||
if (!hdr) {
|
if (!hdr) {
|
||||||
handler_emsg("BasicAuth required, but client didn't send any. 401 Unauth\n");
|
handler_emsg("BasicAuth required, but client didn't send any. 401 Unauth\n");
|
||||||
@@ -908,15 +1178,13 @@ ws_ctx_t *do_handshake(int sock) {
|
|||||||
tmp[len] = '\0';
|
tmp[len] = '\0';
|
||||||
len = ws_b64_pton(tmp, response, 256);
|
len = ws_b64_pton(tmp, response, 256);
|
||||||
|
|
||||||
char authbuf[4096];
|
char authbuf[4096] = "";
|
||||||
strncpy(authbuf, settings.basicauth, 4096);
|
|
||||||
authbuf[4095] = '\0';
|
|
||||||
|
|
||||||
// Do we need to read it from the file?
|
// Do we need to read it from the file?
|
||||||
char *resppw = strchr(response, ':');
|
char *resppw = strchr(response, ':');
|
||||||
if (resppw && *resppw)
|
if (resppw && *resppw)
|
||||||
resppw++;
|
resppw++;
|
||||||
if (!colon[1] && settings.passwdfile) {
|
if (settings.passwdfile) {
|
||||||
if (resppw && *resppw && resppw - response < 32) {
|
if (resppw && *resppw && resppw - response < 32) {
|
||||||
char pwbuf[4096];
|
char pwbuf[4096];
|
||||||
struct kasmpasswd_t *set = readkasmpasswd(settings.passwdfile);
|
struct kasmpasswd_t *set = readkasmpasswd(settings.passwdfile);
|
||||||
@@ -938,6 +1206,9 @@ ws_ctx_t *do_handshake(int sock) {
|
|||||||
snprintf(authbuf, 4096, "%s:%s", set->entries[i].user,
|
snprintf(authbuf, 4096, "%s:%s", set->entries[i].user,
|
||||||
set->entries[i].password);
|
set->entries[i].password);
|
||||||
authbuf[4095] = '\0';
|
authbuf[4095] = '\0';
|
||||||
|
|
||||||
|
if (set->entries[i].owner)
|
||||||
|
owner = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -978,9 +1249,28 @@ ws_ctx_t *do_handshake(int sock) {
|
|||||||
if (!parse_handshake(ws_ctx, handshake)) {
|
if (!parse_handshake(ws_ctx, handshake)) {
|
||||||
handler_emsg("Invalid WS request, maybe a HTTP one\n");
|
handler_emsg("Invalid WS request, maybe a HTTP one\n");
|
||||||
|
|
||||||
|
if (strstr(handshake, "/api/")) {
|
||||||
|
handler_emsg("HTTP request under /api/\n");
|
||||||
|
|
||||||
|
if (owner) {
|
||||||
|
if (ownerapi(ws_ctx, handshake))
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
sprintf(response, "HTTP/1.1 401 Unauthorized\r\n"
|
||||||
|
"Server: KasmVNC/4.0\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Content-type: text/plain\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"401 Unauthorized");
|
||||||
|
ws_send(ws_ctx, response, strlen(response));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.httpdir && settings.httpdir[0])
|
if (settings.httpdir && settings.httpdir[0])
|
||||||
servefile(ws_ctx, handshake);
|
servefile(ws_ctx, handshake);
|
||||||
|
|
||||||
|
done:
|
||||||
free_ws_ctx(ws_ctx);
|
free_ws_ctx(ws_ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#define BUFSIZE 65536
|
#define BUFSIZE 65536
|
||||||
#define DBUFSIZE (BUFSIZE * 3) / 4 - 20
|
#define DBUFSIZE (BUFSIZE * 3) / 4 - 20
|
||||||
@@ -70,10 +71,19 @@ typedef struct {
|
|||||||
unsigned int handler_id;
|
unsigned int handler_id;
|
||||||
const char *cert;
|
const char *cert;
|
||||||
const char *key;
|
const char *key;
|
||||||
const char *basicauth;
|
uint8_t disablebasicauth;
|
||||||
const char *passwdfile;
|
const char *passwdfile;
|
||||||
int ssl_only;
|
int ssl_only;
|
||||||
const char *httpdir;
|
const char *httpdir;
|
||||||
|
|
||||||
|
void *messager;
|
||||||
|
uint8_t *(*screenshotCb)(void *messager, uint16_t w, uint16_t h, const uint8_t q,
|
||||||
|
const uint8_t dedup,
|
||||||
|
uint32_t *len, uint8_t *staging);
|
||||||
|
uint8_t (*adduserCb)(void *messager, const char name[], const char pw[],
|
||||||
|
const uint8_t write);
|
||||||
|
uint8_t (*removeCb)(void *messager, const char name[]);
|
||||||
|
uint8_t (*givecontrolCb)(void *messager, const char name[]);
|
||||||
} settings_t;
|
} settings_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -433,8 +433,7 @@ bool StringParameter::setParam(const char* v) {
|
|||||||
if (immutable) return true;
|
if (immutable) return true;
|
||||||
if (!v)
|
if (!v)
|
||||||
throw rfb::Exception("setParam(<null>) not allowed");
|
throw rfb::Exception("setParam(<null>) not allowed");
|
||||||
if (strcasecmp(getName(), "BasicAuth")) // don't log the auth info
|
vlog.debug("set %s(String) to %s", getName(), v);
|
||||||
vlog.debug("set %s(String) to %s", getName(), v);
|
|
||||||
CharArray oldValue(value);
|
CharArray oldValue(value);
|
||||||
value = strDup(v);
|
value = strDup(v);
|
||||||
return value != 0;
|
return value != 0;
|
||||||
|
|||||||
@@ -966,7 +966,7 @@ static PixelBuffer *bilinearScale(const PixelBuffer *pb, const uint16_t w, const
|
|||||||
return newpb;
|
return newpb;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
|
PixelBuffer *progressiveBilinearScale(const PixelBuffer *pb,
|
||||||
const uint16_t tgtw, const uint16_t tgth,
|
const uint16_t tgtw, const uint16_t tgth,
|
||||||
const float tgtdiff)
|
const float tgtdiff)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ namespace rfb {
|
|||||||
rdr::U32 __unused_attr keycode,
|
rdr::U32 __unused_attr keycode,
|
||||||
bool __unused_attr down) { }
|
bool __unused_attr down) { }
|
||||||
virtual void pointerEvent(const Point& __unused_attr pos,
|
virtual void pointerEvent(const Point& __unused_attr pos,
|
||||||
int __unused_attr buttonMask) { }
|
int __unused_attr buttonMask,
|
||||||
|
const bool __unused_attr skipClick,
|
||||||
|
const bool __unused_attr skipRelease) { }
|
||||||
virtual void clientCutText(const char* __unused_attr str,
|
virtual void clientCutText(const char* __unused_attr str,
|
||||||
int __unused_attr len) { }
|
int __unused_attr len) { }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ void SMsgReader::readPointerEvent()
|
|||||||
int mask = is->readU8();
|
int mask = is->readU8();
|
||||||
int x = is->readU16();
|
int x = is->readU16();
|
||||||
int y = is->readU16();
|
int y = is->readU16();
|
||||||
handler->pointerEvent(Point(x, y), mask);
|
handler->pointerEvent(Point(x, y), mask, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,19 @@ rfb::StringParameter rfb::Server::DLP_ClipLog
|
|||||||
("DLP_Log",
|
("DLP_Log",
|
||||||
"Log clipboard/kbd actions. Accepts off, info or verbose",
|
"Log clipboard/kbd actions. Accepts off, info or verbose",
|
||||||
"off");
|
"off");
|
||||||
|
rfb::StringParameter rfb::Server::DLP_Region
|
||||||
|
("DLP_Region",
|
||||||
|
"Black out anything outside this region",
|
||||||
|
"");
|
||||||
|
|
||||||
|
rfb::BoolParameter rfb::Server::DLP_RegionAllowClick
|
||||||
|
("DLP_RegionAllowClick",
|
||||||
|
"Allow clicks inside the blacked-out region",
|
||||||
|
false);
|
||||||
|
rfb::BoolParameter rfb::Server::DLP_RegionAllowRelease
|
||||||
|
("DLP_RegionAllowRelease",
|
||||||
|
"Allow click releases inside the blacked-out region",
|
||||||
|
true);
|
||||||
|
|
||||||
rfb::StringParameter rfb::Server::maxVideoResolution
|
rfb::StringParameter rfb::Server::maxVideoResolution
|
||||||
("MaxVideoResolution",
|
("MaxVideoResolution",
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ namespace rfb {
|
|||||||
static IntParameter DLP_ClipDelay;
|
static IntParameter DLP_ClipDelay;
|
||||||
static IntParameter DLP_KeyRateLimit;
|
static IntParameter DLP_KeyRateLimit;
|
||||||
static StringParameter DLP_ClipLog;
|
static StringParameter DLP_ClipLog;
|
||||||
|
static StringParameter DLP_Region;
|
||||||
|
static BoolParameter DLP_RegionAllowClick;
|
||||||
|
static BoolParameter DLP_RegionAllowRelease;
|
||||||
static IntParameter jpegVideoQuality;
|
static IntParameter jpegVideoQuality;
|
||||||
static IntParameter webpVideoQuality;
|
static IntParameter webpVideoQuality;
|
||||||
static StringParameter maxVideoResolution;
|
static StringParameter maxVideoResolution;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ static LogWriter vlog("VNCSConnST");
|
|||||||
|
|
||||||
static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
|
static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
|
||||||
|
|
||||||
extern rfb::StringParameter basicauth;
|
extern rfb::BoolParameter disablebasicauth;
|
||||||
|
|
||||||
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||||
bool reverse)
|
bool reverse)
|
||||||
@@ -637,7 +637,7 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
|
|||||||
setCursor();
|
setCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
|
void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease)
|
||||||
{
|
{
|
||||||
pointerEventTime = lastEventTime = time(0);
|
pointerEventTime = lastEventTime = time(0);
|
||||||
server->lastUserInputTime = lastEventTime;
|
server->lastUserInputTime = lastEventTime;
|
||||||
@@ -649,7 +649,23 @@ void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
|
|||||||
server->pointerClient = this;
|
server->pointerClient = this;
|
||||||
else
|
else
|
||||||
server->pointerClient = 0;
|
server->pointerClient = 0;
|
||||||
server->desktop->pointerEvent(pointerEventPos, buttonMask);
|
|
||||||
|
bool skipclick = false, skiprelease = false;
|
||||||
|
if (server->DLPRegion.enabled) {
|
||||||
|
rdr::U16 x1, y1, x2, y2;
|
||||||
|
server->translateDLPRegion(x1, y1, x2, y2);
|
||||||
|
|
||||||
|
if (pos.x < x1 || pos.x >= x2 ||
|
||||||
|
pos.y < y1 || pos.y >= y2) {
|
||||||
|
|
||||||
|
if (!Server::DLP_RegionAllowClick)
|
||||||
|
skipclick = true;
|
||||||
|
if (!Server::DLP_RegionAllowRelease)
|
||||||
|
skiprelease = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server->desktop->pointerEvent(pointerEventPos, buttonMask, skipclick, skiprelease);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1028,13 +1044,12 @@ bool VNCSConnectionST::isShiftPressed()
|
|||||||
bool VNCSConnectionST::getPerms(bool &write, bool &owner) const
|
bool VNCSConnectionST::getPerms(bool &write, bool &owner) const
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
const char *colon = strchr(basicauth, ':');
|
if (disablebasicauth) {
|
||||||
if (!colon || colon[1]) {
|
// We're running without basicauth
|
||||||
// We're running without basicauth, or with both user:pass on the command line
|
|
||||||
write = true;
|
write = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (colon && !colon[1] && user[0]) {
|
if (user[0]) {
|
||||||
struct kasmpasswd_t *set = readkasmpasswd(kasmpasswdpath);
|
struct kasmpasswd_t *set = readkasmpasswd(kasmpasswdpath);
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 0; i < set->num; i++) {
|
for (i = 0; i < set->num; i++) {
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ namespace rfb {
|
|||||||
virtual void queryConnection(const char* userName);
|
virtual void queryConnection(const char* userName);
|
||||||
virtual void clientInit(bool shared);
|
virtual void clientInit(bool shared);
|
||||||
virtual void setPixelFormat(const PixelFormat& pf);
|
virtual void setPixelFormat(const PixelFormat& pf);
|
||||||
virtual void pointerEvent(const Point& pos, int buttonMask);
|
virtual void pointerEvent(const Point& pos, int buttonMask, const bool skipClick, const bool skipRelease);
|
||||||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
||||||
virtual void clientCutText(const char* str, int len);
|
virtual void clientCutText(const char* str, int len);
|
||||||
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
|
virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
|
||||||
|
|||||||
@@ -51,6 +51,8 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <network/GetAPI.h>
|
||||||
|
|
||||||
#include <rfb/ComparingUpdateTracker.h>
|
#include <rfb/ComparingUpdateTracker.h>
|
||||||
#include <rfb/KeyRemapper.h>
|
#include <rfb/KeyRemapper.h>
|
||||||
#include <rfb/ListConnInfo.h>
|
#include <rfb/ListConnInfo.h>
|
||||||
@@ -79,23 +81,116 @@ EncCache VNCServerST::encCache;
|
|||||||
//
|
//
|
||||||
|
|
||||||
static char kasmpasswdpath[4096];
|
static char kasmpasswdpath[4096];
|
||||||
extern rfb::StringParameter basicauth;
|
|
||||||
|
|
||||||
// -=- Constructors/Destructor
|
// -=- Constructors/Destructor
|
||||||
|
|
||||||
|
static void mixedPercentages() {
|
||||||
|
slog.error("Mixing percentages and absolute values in DLP_Region is not allowed");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parseRegionPart(const bool percents, rdr::U16 &pcdest, int &dest,
|
||||||
|
char **inptr) {
|
||||||
|
char *nextptr, *ptr;
|
||||||
|
ptr = *inptr;
|
||||||
|
int val = strtol(ptr, &nextptr, 10);
|
||||||
|
if (!*ptr || ptr == nextptr) {
|
||||||
|
slog.error("Invalid value for DLP_Region");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
ptr = nextptr;
|
||||||
|
if (*ptr == '%') {
|
||||||
|
if (!percents)
|
||||||
|
mixedPercentages();
|
||||||
|
pcdest = val;
|
||||||
|
|
||||||
|
if (val < 0 || val > 100) {
|
||||||
|
slog.error("Percent must be 0-100");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr++;
|
||||||
|
} else if (percents) {
|
||||||
|
mixedPercentages();
|
||||||
|
}
|
||||||
|
dest = val;
|
||||||
|
|
||||||
|
for (; *ptr && *ptr == ','; ptr++);
|
||||||
|
|
||||||
|
*inptr = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||||
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
: blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
|
||||||
blockCounter(0), pb(0), ledState(ledUnknown),
|
blockCounter(0), pb(0), blackedpb(0), ledState(ledUnknown),
|
||||||
name(strDup(name_)), pointerClient(0), comparer(0),
|
name(strDup(name_)), pointerClient(0), comparer(0),
|
||||||
cursor(new Cursor(0, 0, Point(), NULL)),
|
cursor(new Cursor(0, 0, Point(), NULL)),
|
||||||
renderedCursorInvalid(false),
|
renderedCursorInvalid(false),
|
||||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||||
lastConnectionTime(0), disableclients(false),
|
lastConnectionTime(0), disableclients(false),
|
||||||
frameTimer(this)
|
frameTimer(this), apimessager(NULL)
|
||||||
{
|
{
|
||||||
lastUserInputTime = lastDisconnectTime = time(0);
|
lastUserInputTime = lastDisconnectTime = time(0);
|
||||||
slog.debug("creating single-threaded server %s", name.buf);
|
slog.debug("creating single-threaded server %s", name.buf);
|
||||||
|
|
||||||
|
DLPRegion.enabled = DLPRegion.percents = false;
|
||||||
|
|
||||||
|
if (Server::DLP_Region[0]) {
|
||||||
|
unsigned len = strlen(Server::DLP_Region);
|
||||||
|
unsigned i;
|
||||||
|
unsigned commas = 0;
|
||||||
|
int val;
|
||||||
|
char *ptr, *nextptr;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (Server::DLP_Region[i] == ',')
|
||||||
|
commas++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commas != 3) {
|
||||||
|
slog.error("DLP_Region must contain four values");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr = (char *) (const char *) Server::DLP_Region;
|
||||||
|
|
||||||
|
val = strtol(ptr, &nextptr, 10);
|
||||||
|
if (!*ptr || ptr == nextptr) {
|
||||||
|
slog.error("Invalid value for DLP_Region");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
ptr = nextptr;
|
||||||
|
if (*ptr == '%') {
|
||||||
|
DLPRegion.percents = true;
|
||||||
|
DLPRegion.pcx1 = val;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
DLPRegion.x1 = val;
|
||||||
|
|
||||||
|
for (; *ptr && *ptr == ','; ptr++);
|
||||||
|
|
||||||
|
parseRegionPart(DLPRegion.percents, DLPRegion.pcy1, DLPRegion.y1,
|
||||||
|
&ptr);
|
||||||
|
parseRegionPart(DLPRegion.percents, DLPRegion.pcx2, DLPRegion.x2,
|
||||||
|
&ptr);
|
||||||
|
parseRegionPart(DLPRegion.percents, DLPRegion.pcy2, DLPRegion.y2,
|
||||||
|
&ptr);
|
||||||
|
|
||||||
|
// Validity checks
|
||||||
|
if (!DLPRegion.percents) {
|
||||||
|
if (DLPRegion.x1 > 0 && DLPRegion.x2 > 0 && DLPRegion.x2 <= DLPRegion.x1) {
|
||||||
|
slog.error("DLP_Region x2 must be > x1");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (DLPRegion.y1 > 0 && DLPRegion.y2 > 0 && DLPRegion.y2 <= DLPRegion.y1) {
|
||||||
|
slog.error("DLP_Region y2 must be > y1");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DLPRegion.enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
kasmpasswdpath[0] = '\0';
|
kasmpasswdpath[0] = '\0';
|
||||||
wordexp_t wexp;
|
wordexp_t wexp;
|
||||||
if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD))
|
if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD))
|
||||||
@@ -639,6 +734,138 @@ int VNCServerST::msToNextUpdate()
|
|||||||
return frameTimer.getRemainingMs();
|
return frameTimer.getRemainingMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
||||||
|
{
|
||||||
|
if (pthread_mutex_lock(&apimessager->userMutex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const unsigned num = apimessager->actionQueue.size();
|
||||||
|
unsigned i;
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
slog.info("Main thread processing user API request %u/%u", i + 1, num);
|
||||||
|
|
||||||
|
const network::GetAPIMessager::action_data &act = apimessager->actionQueue[i];
|
||||||
|
struct kasmpasswd_t *set = NULL;
|
||||||
|
unsigned s;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
switch (act.action) {
|
||||||
|
case network::GetAPIMessager::USER_REMOVE:
|
||||||
|
set = readkasmpasswd(kasmpasswdpath);
|
||||||
|
found = false;
|
||||||
|
for (s = 0; s < set->num; s++) {
|
||||||
|
if (!strcmp(set->entries[s].user, act.data.user)) {
|
||||||
|
set->entries[s].user[0] = '\0';
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
writekasmpasswd(kasmpasswdpath, set);
|
||||||
|
slog.info("User %s removed", act.data.user);
|
||||||
|
} else {
|
||||||
|
slog.error("Tried to remove nonexistent user %s", act.data.user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case network::GetAPIMessager::USER_GIVE_CONTROL:
|
||||||
|
set = readkasmpasswd(kasmpasswdpath);
|
||||||
|
found = false;
|
||||||
|
for (s = 0; s < set->num; s++) {
|
||||||
|
if (!strcmp(set->entries[s].user, act.data.user)) {
|
||||||
|
set->entries[s].write = 1;
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
set->entries[s].write = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
writekasmpasswd(kasmpasswdpath, set);
|
||||||
|
slog.info("User %s given control", act.data.user);
|
||||||
|
} else {
|
||||||
|
slog.error("Tried to give control to nonexistent user %s", act.data.user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set) {
|
||||||
|
free(set->entries);
|
||||||
|
free(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apimessager->actionQueue.clear();
|
||||||
|
pthread_mutex_unlock(&apimessager->userMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCServerST::translateDLPRegion(rdr::U16 &x1, rdr::U16 &y1, rdr::U16 &x2, rdr::U16 &y2) const
|
||||||
|
{
|
||||||
|
if (DLPRegion.percents) {
|
||||||
|
x1 = DLPRegion.pcx1 ? DLPRegion.pcx1 * pb->getRect().width() / 100 : 0;
|
||||||
|
y1 = DLPRegion.pcy1 ? DLPRegion.pcy1 * pb->getRect().height() / 100 : 0;
|
||||||
|
x2 = DLPRegion.pcx2 ? (100 - DLPRegion.pcx2) * pb->getRect().width() / 100 : pb->getRect().width();
|
||||||
|
y2 = DLPRegion.pcy2 ? (100 - DLPRegion.pcy2) * pb->getRect().height() / 100 : pb->getRect().height();
|
||||||
|
} else {
|
||||||
|
x1 = abs(DLPRegion.x1);
|
||||||
|
y1 = abs(DLPRegion.y1);
|
||||||
|
x2 = pb->getRect().width();
|
||||||
|
y2 = pb->getRect().height();
|
||||||
|
|
||||||
|
if (DLPRegion.x2 < 0)
|
||||||
|
x2 += DLPRegion.x2;
|
||||||
|
else if (DLPRegion.x2 > 0)
|
||||||
|
x2 = DLPRegion.x2;
|
||||||
|
|
||||||
|
if (DLPRegion.y2 < 0)
|
||||||
|
y2 += DLPRegion.y2;
|
||||||
|
else if (DLPRegion.y2 > 0)
|
||||||
|
y2 = DLPRegion.y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y2 > pb->getRect().height())
|
||||||
|
y2 = pb->getRect().height() - 1;
|
||||||
|
if (x2 > pb->getRect().width())
|
||||||
|
x2 = pb->getRect().width() - 1;
|
||||||
|
|
||||||
|
//slog.info("DLP_Region vals %u,%u %u,%u", x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VNCServerST::blackOut()
|
||||||
|
{
|
||||||
|
// Compute the region, since the resolution may have changed
|
||||||
|
rdr::U16 x1, y1, x2, y2;
|
||||||
|
|
||||||
|
translateDLPRegion(x1, y1, x2, y2);
|
||||||
|
|
||||||
|
if (blackedpb)
|
||||||
|
delete blackedpb;
|
||||||
|
blackedpb = new ManagedPixelBuffer(pb->getPF(), pb->getRect().width(), pb->getRect().height());
|
||||||
|
|
||||||
|
int stride;
|
||||||
|
const rdr::U8 *src = pb->getBuffer(pb->getRect(), &stride);
|
||||||
|
rdr::U8 *data = blackedpb->getBufferRW(pb->getRect(), &stride);
|
||||||
|
stride *= 4;
|
||||||
|
|
||||||
|
memcpy(data, src, stride * pb->getRect().height());
|
||||||
|
|
||||||
|
rdr::U16 y;
|
||||||
|
const rdr::U16 w = pb->getRect().width();
|
||||||
|
const rdr::U16 h = pb->getRect().height();
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
if (y < y1 || y > y2) {
|
||||||
|
memset(data, 0, stride);
|
||||||
|
} else {
|
||||||
|
if (x1)
|
||||||
|
memset(data, 0, x1 * 4);
|
||||||
|
if (x2)
|
||||||
|
memset(&data[x2 * 4], 0, (w - x2) * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
data += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// writeUpdate() is called on a regular interval in order to see what
|
// writeUpdate() is called on a regular interval in order to see what
|
||||||
// updates are pending and propagates them to the update tracker for
|
// updates are pending and propagates them to the update tracker for
|
||||||
// each client. It uses the ComparingUpdateTracker's compare() method
|
// each client. It uses the ComparingUpdateTracker's compare() method
|
||||||
@@ -656,6 +883,11 @@ void VNCServerST::writeUpdate()
|
|||||||
assert(blockCounter == 0);
|
assert(blockCounter == 0);
|
||||||
assert(desktopStarted);
|
assert(desktopStarted);
|
||||||
|
|
||||||
|
if (DLPRegion.enabled) {
|
||||||
|
comparer->enable_copyrect(false);
|
||||||
|
blackOut();
|
||||||
|
}
|
||||||
|
|
||||||
comparer->getUpdateInfo(&ui, pb->getRect());
|
comparer->getUpdateInfo(&ui, pb->getRect());
|
||||||
toCheck = ui.changed.union_(ui.copied);
|
toCheck = ui.changed.union_(ui.copied);
|
||||||
|
|
||||||
@@ -709,6 +941,12 @@ void VNCServerST::writeUpdate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (apimessager) {
|
||||||
|
apimessager->mainUpdateScreen(pb);
|
||||||
|
|
||||||
|
checkAPIMessages(apimessager);
|
||||||
|
}
|
||||||
|
|
||||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||||
ci_next = ci; ci_next++;
|
ci_next = ci; ci_next++;
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ namespace rfb {
|
|||||||
class ListConnInfo;
|
class ListConnInfo;
|
||||||
class PixelBuffer;
|
class PixelBuffer;
|
||||||
class KeyRemapper;
|
class KeyRemapper;
|
||||||
|
class network::GetAPIMessager;
|
||||||
|
|
||||||
class VNCServerST : public VNCServer,
|
class VNCServerST : public VNCServer,
|
||||||
public Timer::Callback,
|
public Timer::Callback,
|
||||||
@@ -94,7 +95,7 @@ namespace rfb {
|
|||||||
virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout);
|
virtual void setPixelBuffer(PixelBuffer* pb, const ScreenSet& layout);
|
||||||
virtual void setPixelBuffer(PixelBuffer* pb);
|
virtual void setPixelBuffer(PixelBuffer* pb);
|
||||||
virtual void setScreenLayout(const ScreenSet& layout);
|
virtual void setScreenLayout(const ScreenSet& layout);
|
||||||
virtual PixelBuffer* getPixelBuffer() const { return pb; }
|
virtual PixelBuffer* getPixelBuffer() const { if (DLPRegion.enabled && blackedpb) return blackedpb; else return pb; }
|
||||||
virtual void serverCutText(const char* str, int len);
|
virtual void serverCutText(const char* str, int len);
|
||||||
virtual void add_changed(const Region ®ion);
|
virtual void add_changed(const Region ®ion);
|
||||||
virtual void add_copied(const Region &dest, const Point &delta);
|
virtual void add_copied(const Region &dest, const Point &delta);
|
||||||
@@ -186,6 +187,8 @@ namespace rfb {
|
|||||||
bool getDisable() { return disableclients;};
|
bool getDisable() { return disableclients;};
|
||||||
void setDisable(bool disable) { disableclients = disable;};
|
void setDisable(bool disable) { disableclients = disable;};
|
||||||
|
|
||||||
|
void setAPIMessager(network::GetAPIMessager *msgr) { apimessager = msgr; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
friend class VNCSConnectionST;
|
friend class VNCSConnectionST;
|
||||||
@@ -206,6 +209,7 @@ namespace rfb {
|
|||||||
bool desktopStarted;
|
bool desktopStarted;
|
||||||
int blockCounter;
|
int blockCounter;
|
||||||
PixelBuffer* pb;
|
PixelBuffer* pb;
|
||||||
|
ManagedPixelBuffer *blackedpb;
|
||||||
ScreenSet screenLayout;
|
ScreenSet screenLayout;
|
||||||
unsigned int ledState;
|
unsigned int ledState;
|
||||||
|
|
||||||
@@ -232,6 +236,7 @@ namespace rfb {
|
|||||||
void stopFrameClock();
|
void stopFrameClock();
|
||||||
int msToNextUpdate();
|
int msToNextUpdate();
|
||||||
void writeUpdate();
|
void writeUpdate();
|
||||||
|
void blackOut();
|
||||||
Region getPendingRegion();
|
Region getPendingRegion();
|
||||||
const RenderedCursor* getRenderedCursor();
|
const RenderedCursor* getRenderedCursor();
|
||||||
|
|
||||||
@@ -251,6 +256,17 @@ namespace rfb {
|
|||||||
Timer frameTimer;
|
Timer frameTimer;
|
||||||
|
|
||||||
int inotifyfd;
|
int inotifyfd;
|
||||||
|
|
||||||
|
network::GetAPIMessager *apimessager;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled;
|
||||||
|
int x1, y1, x2, y2;
|
||||||
|
bool percents;
|
||||||
|
rdr::U16 pcx1, pcy1, pcx2, pcy2;
|
||||||
|
} DLPRegion;
|
||||||
|
|
||||||
|
void translateDLPRegion(rdr::U16 &x1, rdr::U16 &y1, rdr::U16 &x2, rdr::U16 &y2) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -465,8 +465,7 @@ sub LoadConfig {
|
|||||||
}
|
}
|
||||||
# change username option to basicAuth and add colon as required by Xvnc, password will be taken from file
|
# change username option to basicAuth and add colon as required by Xvnc, password will be taken from file
|
||||||
if ($k = "username") {
|
if ($k = "username") {
|
||||||
$config{"basicauth"} = "$v:";
|
next;
|
||||||
$vncUserName = $v;
|
|
||||||
} else {
|
} else {
|
||||||
$config{$k} = $v;
|
$config{$k} = $v;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,8 @@ static void enqueueEvents(DeviceIntPtr dev, int n)
|
|||||||
}
|
}
|
||||||
#endif /* XORG < 111 */
|
#endif /* XORG < 111 */
|
||||||
|
|
||||||
void vncPointerButtonAction(int buttonMask)
|
void vncPointerButtonAction(int buttonMask, const unsigned char skipclick,
|
||||||
|
const unsigned char skiprelease)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
#if XORG < 111
|
#if XORG < 111
|
||||||
@@ -169,6 +170,14 @@ void vncPointerButtonAction(int buttonMask)
|
|||||||
if ((buttonMask ^ oldButtonMask) & (1 << i)) {
|
if ((buttonMask ^ oldButtonMask) & (1 << i)) {
|
||||||
int action = (buttonMask & (1<<i)) ?
|
int action = (buttonMask & (1<<i)) ?
|
||||||
ButtonPress : ButtonRelease;
|
ButtonPress : ButtonRelease;
|
||||||
|
|
||||||
|
if (action == ButtonPress && skipclick) {
|
||||||
|
buttonMask &= ~(1<<i);
|
||||||
|
continue;
|
||||||
|
} else if (action == ButtonRelease && skiprelease) {
|
||||||
|
buttonMask |= (1<<i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
#if XORG < 110
|
#if XORG < 110
|
||||||
n = GetPointerEvents(eventq, vncPointerDev,
|
n = GetPointerEvents(eventq, vncPointerDev,
|
||||||
action, i + 1,
|
action, i + 1,
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ extern "C" {
|
|||||||
|
|
||||||
void vncInitInputDevice(void);
|
void vncInitInputDevice(void);
|
||||||
|
|
||||||
void vncPointerButtonAction(int buttonMask);
|
void vncPointerButtonAction(int buttonMask, const unsigned char skipclick,
|
||||||
|
const unsigned char skiprelease);
|
||||||
void vncPointerMove(int x, int y);
|
void vncPointerMove(int x, int y);
|
||||||
void vncGetPointerPos(int *x, int *y);
|
void vncGetPointerPos(int *x, int *y);
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ XserverDesktop::XserverDesktop(int screenIndex_,
|
|||||||
i != listeners.end();
|
i != listeners.end();
|
||||||
i++) {
|
i++) {
|
||||||
vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
|
vncSetNotifyFd((*i)->getFd(), screenIndex, true, false);
|
||||||
|
if ((*i)->getMessager())
|
||||||
|
server->setAPIMessager((*i)->getMessager());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,11 +418,12 @@ void XserverDesktop::approveConnection(uint32_t opaqueId, bool accept,
|
|||||||
// SDesktop callbacks
|
// SDesktop callbacks
|
||||||
|
|
||||||
|
|
||||||
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask)
|
void XserverDesktop::pointerEvent(const Point& pos, int buttonMask,
|
||||||
|
const bool skipClick, const bool skipRelease)
|
||||||
{
|
{
|
||||||
vncPointerMove(pos.x + vncGetScreenX(screenIndex),
|
vncPointerMove(pos.x + vncGetScreenX(screenIndex),
|
||||||
pos.y + vncGetScreenY(screenIndex));
|
pos.y + vncGetScreenY(screenIndex));
|
||||||
vncPointerButtonAction(buttonMask);
|
vncPointerButtonAction(buttonMask, skipClick, skipRelease);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XserverDesktop::clientCutText(const char* str, int len)
|
void XserverDesktop::clientCutText(const char* str, int len)
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ public:
|
|||||||
const char* rejectMsg=0);
|
const char* rejectMsg=0);
|
||||||
|
|
||||||
// rfb::SDesktop callbacks
|
// rfb::SDesktop callbacks
|
||||||
virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
|
virtual void pointerEvent(const rfb::Point& pos, int buttonMask,
|
||||||
|
const bool skipClick, const bool skipRelease);
|
||||||
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
virtual void keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down);
|
||||||
virtual void clientCutText(const char* str, int len);
|
virtual void clientCutText(const char* str, int len);
|
||||||
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
|
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
|
||||||
|
|||||||
@@ -280,6 +280,20 @@ Kasm exposes a few settings to the client the standard VNC does not.
|
|||||||
This param lets the server ignore those.
|
This param lets the server ignore those.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
.B \-DLP_Region \fIx1,y1,x2,y2\fP
|
||||||
|
Black out anything outside this region. x1,y1 is the upper-left corner,
|
||||||
|
and x2,y2 the lower-left. In addition to absolute pixel values, percentages
|
||||||
|
are allowed, zero means "default", and a negative number means "border".
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.B \-DLP_RegionAllowClick \fIbool\fP
|
||||||
|
Allow clicks inside the blacked-out region.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
|
.B \-DLP_RegionAllowRelease \fIbool\fP
|
||||||
|
Allow click releases inside the blacked-out region.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
.B \-DLP_ClipSendMax \fIbytes\fP
|
.B \-DLP_ClipSendMax \fIbytes\fP
|
||||||
Limit clipboard bytes to send to clients in one transaction. Default 10,000.
|
Limit clipboard bytes to send to clients in one transaction. Default 10,000.
|
||||||
0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely.
|
0 disables the limit, use \fBSendCutText\fP to disable clipboard sending entirely.
|
||||||
@@ -325,9 +339,9 @@ are in the same file, use \fB-cert\fP.
|
|||||||
Require SSL for websocket connections. Default off, non-SSL allowed.
|
Require SSL for websocket connections. Default off, non-SSL allowed.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.B \-basicAuth \fIuser:pass\fP
|
.B \-disableBasicAuth
|
||||||
Username and password for websocket connections. Default empty, no authentication required.
|
Disable basic auth for websocket connections. Default enabled, details read from
|
||||||
If the password is empty, read it from the \fB-KasmPasswordFile\fP.
|
the \fB-KasmPasswordFile\fP.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
.B \-SecurityTypes \fIsec-types\fP
|
.B \-SecurityTypes \fIsec-types\fP
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ rfb::IntParameter websocketPort("websocketPort", "websocket port to listen for",
|
|||||||
rfb::StringParameter cert("cert", "SSL pem cert to use for websocket connections", "");
|
rfb::StringParameter cert("cert", "SSL pem cert to use for websocket connections", "");
|
||||||
rfb::StringParameter certkey("key", "SSL pem key to use for websocket connections (if separate)", "");
|
rfb::StringParameter certkey("key", "SSL pem key to use for websocket connections (if separate)", "");
|
||||||
rfb::BoolParameter sslonly("sslOnly", "Require SSL for websockets", false);
|
rfb::BoolParameter sslonly("sslOnly", "Require SSL for websockets", false);
|
||||||
rfb::StringParameter basicauth("BasicAuth", "user:pass for HTTP basic auth for websockets", "");
|
rfb::BoolParameter disablebasicauth("DisableBasicAuth", "Disable basic auth for websockets", false);
|
||||||
rfb::StringParameter interface("interface",
|
rfb::StringParameter interface("interface",
|
||||||
"listen on the specified network address",
|
"listen on the specified network address",
|
||||||
"all");
|
"all");
|
||||||
@@ -225,7 +225,7 @@ void vncExtensionInit(void)
|
|||||||
if (!noWebsocket)
|
if (!noWebsocket)
|
||||||
network::createWebsocketListeners(&listeners, websocketPort,
|
network::createWebsocketListeners(&listeners, websocketPort,
|
||||||
localhostOnly ? "local" : addr,
|
localhostOnly ? "local" : addr,
|
||||||
sslonly, cert, certkey, basicauth, httpDir);
|
sslonly, cert, certkey, disablebasicauth, httpDir);
|
||||||
else if (localhostOnly)
|
else if (localhostOnly)
|
||||||
network::createLocalTcpListeners(&listeners, port);
|
network::createLocalTcpListeners(&listeners, port);
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user