Merge branch 'users' into packages_and_multiuser_passwd
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common ${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
||||
|
||||
set(NETWORK_SOURCES
|
||||
Socket.cxx
|
||||
TcpSocket.cxx
|
||||
websocket.c
|
||||
websockify.c)
|
||||
websockify.c
|
||||
${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd/kasmpasswd.c)
|
||||
|
||||
if(NOT WIN32)
|
||||
set(NETWORK_SOURCES ${NETWORK_SOURCES} UnixSocket.cxx)
|
||||
|
||||
@@ -124,11 +124,20 @@ WebSocket::WebSocket(int sock) : Socket(sock)
|
||||
}
|
||||
|
||||
char* WebSocket::getPeerAddress() {
|
||||
return rfb::strDup("websocket");
|
||||
struct sockaddr_un addr;
|
||||
socklen_t len = sizeof(struct sockaddr_un);
|
||||
if (getpeername(getFd(), (struct sockaddr *) &addr, &len) != 0) {
|
||||
vlog.error("unable to get peer name for socket");
|
||||
return rfb::strDup("websocket");
|
||||
}
|
||||
return rfb::strDup(addr.sun_path + 1);
|
||||
}
|
||||
|
||||
char* WebSocket::getPeerEndpoint() {
|
||||
return rfb::strDup("websocket");
|
||||
char buf[1024];
|
||||
sprintf(buf, "%s::websocket", getPeerAddress());
|
||||
|
||||
return rfb::strDup(buf);
|
||||
}
|
||||
|
||||
// -=- TcpSocket
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <openssl/md5.h> /* md5 hash */
|
||||
#include <openssl/sha.h> /* sha1 hash */
|
||||
#include "websocket.h"
|
||||
#include "kasmpasswd.h"
|
||||
|
||||
/*
|
||||
* Global state
|
||||
@@ -59,28 +60,28 @@ void fatal(char *msg)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* resolve host with also IP address parsing */
|
||||
int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
||||
{
|
||||
if (!inet_aton(hostname, sin_addr)) {
|
||||
struct addrinfo *ai, *cur;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
if (getaddrinfo(hostname, NULL, &hints, &ai))
|
||||
return -1;
|
||||
for (cur = ai; cur; cur = cur->ai_next) {
|
||||
if (cur->ai_family == AF_INET) {
|
||||
*sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
|
||||
freeaddrinfo(ai);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* resolve host with also IP address parsing */
|
||||
int resolve_host(struct in_addr *sin_addr, const char *hostname)
|
||||
{
|
||||
if (!inet_aton(hostname, sin_addr)) {
|
||||
struct addrinfo *ai, *cur;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
if (getaddrinfo(hostname, NULL, &hints, &ai))
|
||||
return -1;
|
||||
for (cur = ai; cur; cur = cur->ai_next) {
|
||||
if (cur->ai_family == AF_INET) {
|
||||
*sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
|
||||
freeaddrinfo(ai);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -107,7 +108,7 @@ ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) {
|
||||
|
||||
ws_ctx_t *alloc_ws_ctx() {
|
||||
ws_ctx_t *ctx;
|
||||
if (! (ctx = malloc(sizeof(ws_ctx_t))) )
|
||||
if (! (ctx = calloc(sizeof(ws_ctx_t), 1)) )
|
||||
{ fatal("malloc()"); }
|
||||
|
||||
if (! (ctx->cin_buf = malloc(BUFSIZE)) )
|
||||
@@ -308,7 +309,7 @@ int decode_hixie(char *src, size_t srclength,
|
||||
*left = srclength;
|
||||
|
||||
if (srclength == 2 &&
|
||||
(src[0] == '\xff') &&
|
||||
(src[0] == '\xff') &&
|
||||
(src[1] == '\x00')) {
|
||||
// client sent orderly close frame
|
||||
*opcode = 0x8; // Close frame
|
||||
@@ -326,7 +327,7 @@ int decode_hixie(char *src, size_t srclength,
|
||||
return len;
|
||||
}
|
||||
retlen += len;
|
||||
start = end + 2; // Skip '\xff' end and '\x00' start
|
||||
start = end + 2; // Skip '\xff' end and '\x00' start
|
||||
framecount++;
|
||||
} while (end < (src+srclength-1));
|
||||
if (framecount > 1) {
|
||||
@@ -399,7 +400,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
||||
int i = 0, len, framecount = 0;
|
||||
size_t remaining = 0;
|
||||
unsigned int target_offset = 0, hdr_length = 0, payload_length = 0;
|
||||
|
||||
|
||||
*left = srclength;
|
||||
frame = src;
|
||||
|
||||
@@ -499,7 +500,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
||||
snprintf(cntstr, 3, "%d", framecount);
|
||||
traffic(cntstr);
|
||||
}
|
||||
|
||||
|
||||
*left = remaining;
|
||||
return target_offset;
|
||||
}
|
||||
@@ -543,7 +544,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
end = strstr(start, "\r\n");
|
||||
strncpy(headers->origin, start, end-start);
|
||||
headers->origin[end-start] = '\0';
|
||||
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Version: ");
|
||||
if (start) {
|
||||
// HyBi/RFC 6455
|
||||
@@ -560,14 +561,14 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
end = strstr(start, "\r\n");
|
||||
strncpy(headers->key1, start, end-start);
|
||||
headers->key1[end-start] = '\0';
|
||||
|
||||
|
||||
start = strstr(handshake, "\r\nConnection: ");
|
||||
if (!start) { return 0; }
|
||||
start += 14;
|
||||
end = strstr(start, "\r\n");
|
||||
strncpy(headers->connection, start, end-start);
|
||||
headers->connection[end-start] = '\0';
|
||||
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Protocol: ");
|
||||
if (!start) { return 0; }
|
||||
start += 26;
|
||||
@@ -592,7 +593,7 @@ int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) {
|
||||
end = strstr(start, "\r\n");
|
||||
strncpy(headers->key1, start, end-start);
|
||||
headers->key1[end-start] = '\0';
|
||||
|
||||
|
||||
start = strstr(handshake, "\r\nSec-WebSocket-Key2: ");
|
||||
if (!start) { return 0; }
|
||||
start += 22;
|
||||
@@ -916,34 +917,43 @@ ws_ctx_t *do_handshake(int sock) {
|
||||
if (resppw && *resppw)
|
||||
resppw++;
|
||||
if (!colon[1] && settings.passwdfile) {
|
||||
if (resppw && *resppw) {
|
||||
if (resppw && *resppw && resppw - response < 32) {
|
||||
char pwbuf[4096];
|
||||
FILE *f = fopen(settings.passwdfile, "r");
|
||||
if (f) {
|
||||
handler_emsg("BasicAuth reading password from %s\n", settings.passwdfile);
|
||||
const unsigned len = fread(pwbuf, 1, 4096, f);
|
||||
fclose(f);
|
||||
pwbuf[4095] = '\0';
|
||||
if (len < 4096)
|
||||
pwbuf[len] = '\0';
|
||||
|
||||
snprintf(authbuf, 4096, "%s%s", settings.basicauth, pwbuf);
|
||||
authbuf[4095] = '\0';
|
||||
|
||||
struct crypt_data cdata;
|
||||
cdata.initialized = 0;
|
||||
|
||||
const char *encrypted = crypt_r(resppw, "$5$kasm$", &cdata);
|
||||
*resppw = '\0';
|
||||
|
||||
snprintf(pwbuf, 4096, "%s%s", response, encrypted);
|
||||
pwbuf[4095] = '\0';
|
||||
strcpy(response, pwbuf);
|
||||
} else {
|
||||
fprintf(stderr, " websocket %d: Error: BasicAuth configured to read password from file %s, but the file doesn't exist\n",
|
||||
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,
|
||||
settings.passwdfile);
|
||||
} else {
|
||||
unsigned i;
|
||||
char inuser[32];
|
||||
unsigned char found = 0;
|
||||
memcpy(inuser, response, resppw - response - 1);
|
||||
inuser[resppw - response - 1] = '\0';
|
||||
|
||||
for (i = 0; i < set->num; i++) {
|
||||
if (!strcmp(set->entries[i].user, inuser)) {
|
||||
found = 1;
|
||||
strcpy(ws_ctx->user, inuser);
|
||||
snprintf(authbuf, 4096, "%s:%s", set->entries[i].user,
|
||||
set->entries[i].password);
|
||||
authbuf[4095] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
handler_emsg("BasicAuth user %s not found\n", inuser);
|
||||
}
|
||||
free(set->entries);
|
||||
free(set);
|
||||
|
||||
const char *encrypted = crypt(resppw, "$5$kasm$");
|
||||
*resppw = '\0';
|
||||
|
||||
snprintf(pwbuf, 4096, "%s%s", response, encrypted);
|
||||
pwbuf[4095] = '\0';
|
||||
strcpy(response, pwbuf);
|
||||
} else {
|
||||
// Client tried an empty password, just fail them
|
||||
response[0] = '\0';
|
||||
@@ -1001,7 +1011,7 @@ ws_ctx_t *do_handshake(int sock) {
|
||||
snprintf(response, sizeof(response), SERVER_HANDSHAKE_HIXIE, pre, headers->origin,
|
||||
pre, scheme, headers->host, headers->path, pre, "base64", trailer);
|
||||
}
|
||||
|
||||
|
||||
//handler_msg("response: %s\n", response);
|
||||
ws_send(ws_ctx, response, strlen(response));
|
||||
|
||||
@@ -1018,7 +1028,6 @@ void *subthread(void *ptr) {
|
||||
|
||||
const int csock = pass->csock;
|
||||
wsthread_handler_id = pass->id;
|
||||
free((void *) pass);
|
||||
|
||||
ws_ctx_t *ws_ctx;
|
||||
|
||||
@@ -1028,11 +1037,14 @@ void *subthread(void *ptr) {
|
||||
goto out; // Child process exits
|
||||
}
|
||||
|
||||
memcpy(ws_ctx->ip, pass->ip, sizeof(pass->ip));
|
||||
|
||||
proxy_handler(ws_ctx);
|
||||
if (pipe_error) {
|
||||
handler_emsg("Closing due to SIGPIPE\n");
|
||||
}
|
||||
out:
|
||||
free((void *) pass);
|
||||
|
||||
if (ws_ctx) {
|
||||
ws_socket_free(ws_ctx);
|
||||
@@ -1068,12 +1080,13 @@ void *start_server(void *unused) {
|
||||
error("ERROR on accept");
|
||||
continue;
|
||||
}
|
||||
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,
|
||||
inet_ntoa(cli_addr.sin_addr));
|
||||
pass->ip);
|
||||
|
||||
pthread_t tid;
|
||||
struct wspass_t *pass = calloc(1, sizeof(struct wspass_t));
|
||||
pass->id = settings.handler_id;
|
||||
pass->csock = csock;
|
||||
pthread_create(&tid, NULL, subthread, pass);
|
||||
|
||||
@@ -53,11 +53,15 @@ typedef struct {
|
||||
char *cout_buf;
|
||||
char *tin_buf;
|
||||
char *tout_buf;
|
||||
|
||||
char user[32];
|
||||
char ip[64];
|
||||
} ws_ctx_t;
|
||||
|
||||
struct wspass_t {
|
||||
int csock;
|
||||
unsigned id;
|
||||
char ip[64];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -227,7 +227,13 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||
strcpy(addr.sun_path, ".KasmVNCSock");
|
||||
addr.sun_path[0] = '\0';
|
||||
|
||||
struct sockaddr_un myaddr;
|
||||
myaddr.sun_family = AF_UNIX;
|
||||
sprintf(myaddr.sun_path, ".%s@%s", ws_ctx->user, ws_ctx->ip);
|
||||
myaddr.sun_path[0] = '\0';
|
||||
|
||||
int tsock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
bind(tsock, (struct sockaddr *) &myaddr, sizeof(struct sockaddr_un));
|
||||
|
||||
handler_msg("connecting to VNC target\n");
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR})
|
||||
include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/unix/kasmvncpasswd)
|
||||
|
||||
set(RFB_SOURCES
|
||||
Blacklist.cxx
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <rfb/ledStates.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/SMsgHandler.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
using namespace rfb;
|
||||
@@ -43,7 +44,7 @@ ConnParams::ConnParams()
|
||||
supportsContinuousUpdates(false),
|
||||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
||||
subsampling(subsampleUndefined), name_(0), verStrPos(0),
|
||||
ledState_(ledUnknown)
|
||||
ledState_(ledUnknown), shandler(NULL)
|
||||
{
|
||||
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
||||
setName("");
|
||||
@@ -124,6 +125,8 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
encodings_.clear();
|
||||
encodings_.insert(encodingRaw);
|
||||
|
||||
bool canChangeSettings = !shandler || shandler->canChangeKasmSettings();
|
||||
|
||||
for (int i = nEncodings-1; i >= 0; i--) {
|
||||
switch (encodings[i]) {
|
||||
case encodingCopyRect:
|
||||
@@ -184,11 +187,11 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
subsampling = subsample16X;
|
||||
break;
|
||||
case pseudoEncodingPreferBandwidth:
|
||||
if (!rfb::Server::ignoreClientSettingsKasm)
|
||||
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings)
|
||||
Server::preferBandwidth.setParam();
|
||||
break;
|
||||
case pseudoEncodingMaxVideoResolution:
|
||||
if (!rfb::Server::ignoreClientSettingsKasm)
|
||||
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings)
|
||||
kasmPassed[KASM_MAX_VIDEO_RESOLUTION] = true;
|
||||
break;
|
||||
}
|
||||
@@ -205,7 +208,7 @@ void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
encodings[i] <= pseudoEncodingFineQualityLevel100)
|
||||
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;
|
||||
|
||||
if (!rfb::Server::ignoreClientSettingsKasm) {
|
||||
if (!rfb::Server::ignoreClientSettingsKasm && canChangeSettings) {
|
||||
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9)
|
||||
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
|
||||
|
||||
@@ -42,6 +42,8 @@ namespace rfb {
|
||||
const int subsample8X = 4;
|
||||
const int subsample16X = 5;
|
||||
|
||||
class SMsgHandler;
|
||||
|
||||
class ConnParams {
|
||||
public:
|
||||
ConnParams();
|
||||
@@ -74,6 +76,8 @@ namespace rfb {
|
||||
const PixelFormat& pf() const { return pf_; }
|
||||
void setPF(const PixelFormat& pf);
|
||||
|
||||
void setSHandler(SMsgHandler *s) { shandler = s; }
|
||||
|
||||
const char* name() const { return name_; }
|
||||
void setName(const char* name);
|
||||
|
||||
@@ -136,6 +140,7 @@ namespace rfb {
|
||||
char verStr[13];
|
||||
int verStrPos;
|
||||
unsigned int ledState_;
|
||||
SMsgHandler *shandler;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,7 @@ using namespace rfb;
|
||||
|
||||
SMsgHandler::SMsgHandler()
|
||||
{
|
||||
cp.setSHandler(this);
|
||||
}
|
||||
|
||||
SMsgHandler::~SMsgHandler()
|
||||
|
||||
@@ -56,6 +56,8 @@ namespace rfb {
|
||||
|
||||
virtual void sendStats() = 0;
|
||||
|
||||
virtual bool canChangeKasmSettings() const = 0;
|
||||
|
||||
// InputHandler interface
|
||||
// The InputHandler methods will be called for the corresponding messages.
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ void SMsgReader::readSetMaxVideoResolution()
|
||||
width = is->readU16();
|
||||
height = is->readU16();
|
||||
|
||||
if (!rfb::Server::ignoreClientSettingsKasm) {
|
||||
if (!rfb::Server::ignoreClientSettingsKasm && handler->canChangeKasmSettings()) {
|
||||
sprintf(tmp, "%ux%u", width, height);
|
||||
rfb::Server::maxVideoResolution.setParam(tmp);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
#include "kasmpasswd.h"
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
@@ -45,6 +48,8 @@ static LogWriter vlog("VNCSConnST");
|
||||
|
||||
static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
|
||||
|
||||
extern rfb::StringParameter basicauth;
|
||||
|
||||
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
bool reverse)
|
||||
: sock(s), reverseConnection(reverse),
|
||||
@@ -54,7 +59,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
losslessTimer(this), kbdLogTimer(this), server(server_), updates(false),
|
||||
updateRenderedCursor(false), removeRenderedCursor(false),
|
||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||
pointerEventTime(0),
|
||||
needsPermCheck(false), pointerEventTime(0),
|
||||
clientHasCursor(false),
|
||||
accessRights(AccessDefault), startTime(time(0))
|
||||
{
|
||||
@@ -65,6 +70,25 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
memset(bstats_total, 0, sizeof(bstats_total));
|
||||
gettimeofday(&connStart, NULL);
|
||||
|
||||
// Check their permissions, if applicable
|
||||
kasmpasswdpath[0] = '\0';
|
||||
wordexp_t wexp;
|
||||
if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD))
|
||||
strncpy(kasmpasswdpath, wexp.we_wordv[0], 4096);
|
||||
kasmpasswdpath[4095] = '\0';
|
||||
wordfree(&wexp);
|
||||
|
||||
user[0] = '\0';
|
||||
const char *at = strchr(peerEndpoint.buf, '@');
|
||||
if (at && at - peerEndpoint.buf > 1 && at - peerEndpoint.buf < 32) {
|
||||
memcpy(user, peerEndpoint.buf, at - peerEndpoint.buf);
|
||||
user[at - peerEndpoint.buf] = '\0';
|
||||
}
|
||||
|
||||
bool write, owner;
|
||||
if (!getPerms(write, owner) || !write)
|
||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
||||
|
||||
// Configure the socket
|
||||
setSocketTimeouts();
|
||||
lastEventTime = time(0);
|
||||
@@ -1001,6 +1025,34 @@ bool VNCSConnectionST::isShiftPressed()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VNCSConnectionST::getPerms(bool &write, bool &owner) const
|
||||
{
|
||||
bool found = false;
|
||||
const char *colon = strchr(basicauth, ':');
|
||||
if (!colon || colon[1]) {
|
||||
// We're running without basicauth, or with both user:pass on the command line
|
||||
write = true;
|
||||
return true;
|
||||
}
|
||||
if (colon && !colon[1] && user[0]) {
|
||||
struct kasmpasswd_t *set = readkasmpasswd(kasmpasswdpath);
|
||||
unsigned i;
|
||||
for (i = 0; i < set->num; i++) {
|
||||
if (!strcmp(set->entries[i].user, user)) {
|
||||
write = set->entries[i].write;
|
||||
owner = set->entries[i].owner;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(set->entries);
|
||||
free(set);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void VNCSConnectionST::writeRTTPing()
|
||||
{
|
||||
char type;
|
||||
@@ -1081,6 +1133,22 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
||||
if (isCongested())
|
||||
return;
|
||||
|
||||
// Check for permission changes?
|
||||
if (needsPermCheck) {
|
||||
needsPermCheck = false;
|
||||
|
||||
bool write, owner, ret;
|
||||
ret = getPerms(write, owner);
|
||||
if (!ret) {
|
||||
close("User was deleted");
|
||||
return;
|
||||
} else if (!write) {
|
||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize));
|
||||
} else {
|
||||
accessRights |= AccessPtrEvents | AccessKeyEvents | AccessSetDesktopSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates often consists of many small writes, and in continuous
|
||||
// mode, we will also have small fence messages around the update. We
|
||||
// need to aggregate these in order to not clog up TCP's congestion
|
||||
|
||||
@@ -102,6 +102,10 @@ namespace rfb {
|
||||
// or because the current cursor position has not been set by this client.
|
||||
bool needRenderedCursor();
|
||||
|
||||
void recheckPerms() {
|
||||
needsPermCheck = true;
|
||||
}
|
||||
|
||||
network::Socket* getSock() { return sock; }
|
||||
void add_changed(const Region& region) { updates.add_changed(region); }
|
||||
void add_changed_all() { updates.add_changed(server->pb->getRect()); }
|
||||
@@ -179,12 +183,23 @@ namespace rfb {
|
||||
virtual void supportsLEDState();
|
||||
|
||||
virtual void sendStats();
|
||||
virtual bool canChangeKasmSettings() const {
|
||||
return (accessRights & (AccessPtrEvents | AccessKeyEvents)) ==
|
||||
(AccessPtrEvents | AccessKeyEvents);
|
||||
}
|
||||
|
||||
// setAccessRights() allows a security package to limit the access rights
|
||||
// of a VNCSConnectioST to the server. These access rights are applied
|
||||
// such that the actual rights granted are the minimum of the server's
|
||||
// default access settings and the connection's access settings.
|
||||
virtual void setAccessRights(AccessRights ar) {accessRights=ar;}
|
||||
virtual void setAccessRights(AccessRights ar) {
|
||||
accessRights = ar;
|
||||
|
||||
bool write, owner;
|
||||
if (!getPerms(write, owner) || !write)
|
||||
accessRights = (accessRights & ~(AccessPtrEvents | AccessKeyEvents));
|
||||
needsPermCheck = false;
|
||||
}
|
||||
|
||||
// Timer callbacks
|
||||
virtual bool handleTimeout(Timer* t);
|
||||
@@ -193,6 +208,8 @@ namespace rfb {
|
||||
|
||||
bool isShiftPressed();
|
||||
|
||||
bool getPerms(bool &write, bool &owner) const;
|
||||
|
||||
// Congestion control
|
||||
void writeRTTPing();
|
||||
bool isCongested();
|
||||
@@ -249,6 +266,10 @@ namespace rfb {
|
||||
rdr::U64 bstats_total[BS_NUM];
|
||||
struct timeval connStart;
|
||||
|
||||
char user[32];
|
||||
char kasmpasswdpath[4096];
|
||||
bool needsPermCheck;
|
||||
|
||||
time_t lastEventTime;
|
||||
time_t pointerEventTime;
|
||||
Point pointerEventPos;
|
||||
|
||||
@@ -63,6 +63,11 @@
|
||||
|
||||
#include <rdr/types.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter slog("VNCServerST");
|
||||
@@ -73,6 +78,9 @@ EncCache VNCServerST::encCache;
|
||||
// -=- VNCServerST Implementation
|
||||
//
|
||||
|
||||
static char kasmpasswdpath[4096];
|
||||
extern rfb::StringParameter basicauth;
|
||||
|
||||
// -=- Constructors/Destructor
|
||||
|
||||
VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
@@ -87,6 +95,26 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
{
|
||||
lastUserInputTime = lastDisconnectTime = time(0);
|
||||
slog.debug("creating single-threaded server %s", name.buf);
|
||||
|
||||
kasmpasswdpath[0] = '\0';
|
||||
wordexp_t wexp;
|
||||
if (!wordexp(rfb::Server::kasmPasswordFile, &wexp, WRDE_NOCMD))
|
||||
strncpy(kasmpasswdpath, wexp.we_wordv[0], 4096);
|
||||
kasmpasswdpath[4095] = '\0';
|
||||
wordfree(&wexp);
|
||||
|
||||
if (kasmpasswdpath[0] && access(kasmpasswdpath, R_OK) == 0) {
|
||||
// Set up a watch on the password file
|
||||
inotifyfd = inotify_init();
|
||||
if (inotifyfd < 0)
|
||||
slog.error("Failed to init inotify");
|
||||
|
||||
int flags = fcntl(inotifyfd, F_GETFL, 0);
|
||||
fcntl(inotifyfd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||
slog.error("Failed to set watch");
|
||||
}
|
||||
}
|
||||
|
||||
VNCServerST::~VNCServerST()
|
||||
@@ -659,8 +687,34 @@ void VNCServerST::writeUpdate()
|
||||
encCache.clear();
|
||||
encCache.enabled = clients.size() > 1;
|
||||
|
||||
// Check if the password file was updated
|
||||
bool permcheck = false;
|
||||
if (inotifyfd >= 0) {
|
||||
char buf[256];
|
||||
int ret = read(inotifyfd, buf, 256);
|
||||
int pos = 0;
|
||||
while (ret > 0) {
|
||||
const struct inotify_event * const ev = (struct inotify_event *) &buf[pos];
|
||||
|
||||
if (ev->mask & IN_IGNORED) {
|
||||
// file was deleted, set new watch
|
||||
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||
slog.error("Failed to set watch");
|
||||
}
|
||||
|
||||
permcheck = true;
|
||||
|
||||
ret -= sizeof(struct inotify_event) - ev->len;
|
||||
pos += sizeof(struct inotify_event) - ev->len;
|
||||
}
|
||||
}
|
||||
|
||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
|
||||
if (permcheck)
|
||||
(*ci)->recheckPerms();
|
||||
|
||||
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
||||
(*ci)->add_copypassed(ui.copypassed);
|
||||
(*ci)->add_changed(ui.changed);
|
||||
|
||||
@@ -249,6 +249,8 @@ namespace rfb {
|
||||
bool disableclients;
|
||||
|
||||
Timer frameTimer;
|
||||
|
||||
int inotifyfd;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user