Specifies that the server must ignore all keyboard or mouse events sent by the client. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2180903 Signed-off-by: Carlos Santos <casantos@redhat.com>pull/1670/head
static LogWriter vlog("SConnection"); | static LogWriter vlog("SConnection"); | ||||
SConnection::SConnection() | |||||
SConnection::SConnection(AccessRights accessRights) | |||||
: readyForSetColourMapEntries(false), | : readyForSetColourMapEntries(false), | ||||
is(0), os(0), reader_(0), writer_(0), ssecurity(0), | is(0), os(0), reader_(0), writer_(0), ssecurity(0), | ||||
authFailureTimer(this, &SConnection::handleAuthFailureTimeout), | authFailureTimer(this, &SConnection::handleAuthFailureTimeout), | ||||
state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), | state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), | ||||
accessRights(AccessNone), hasRemoteClipboard(false), | |||||
accessRights(accessRights), hasRemoteClipboard(false), | |||||
hasLocalClipboard(false), | hasLocalClipboard(false), | ||||
unsolicitedClipboardAttempt(false) | unsolicitedClipboardAttempt(false) | ||||
{ | { | ||||
} | } | ||||
state_ = RFBSTATE_QUERYING; | state_ = RFBSTATE_QUERYING; | ||||
setAccessRights(ssecurity->getAccessRights()); | |||||
setAccessRights(accessRights & ssecurity->getAccessRights()); | |||||
queryConnection(ssecurity->getUserName()); | queryConnection(ssecurity->getUserName()); | ||||
// If the connection got approved right away then we can continue | // If the connection got approved right away then we can continue |
class SConnection : public SMsgHandler { | class SConnection : public SMsgHandler { | ||||
public: | public: | ||||
SConnection(); | |||||
SConnection(AccessRights accessRights); | |||||
virtual ~SConnection(); | virtual ~SConnection(); | ||||
// Methods to initialise the connection | // Methods to initialise the connection | ||||
// clipboard via handleClipboardRequest(). | // clipboard via handleClipboardRequest(). | ||||
virtual void sendClipboardData(const char* data); | virtual void sendClipboardData(const char* data); | ||||
// getAccessRights() returns the access rights of a SConnection to the server. | |||||
AccessRights getAccessRights() { return accessRights; } | |||||
// setAccessRights() allows a security package to limit the access rights | // setAccessRights() allows a security package to limit the access rights | ||||
// of a SConnection to the server. How the access rights are treated | // of a SConnection to the server. How the access rights are treated | ||||
// is up to the derived class. | // is up to the derived class. |
static Cursor emptyCursor(0, 0, Point(0, 0), NULL); | static Cursor emptyCursor(0, 0, Point(0, 0), NULL); | ||||
VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, | ||||
bool reverse) | |||||
: sock(s), reverseConnection(reverse), | |||||
bool reverse, AccessRights ar) | |||||
: SConnection(ar), | |||||
sock(s), reverseConnection(reverse), | |||||
inProcessMessages(false), | inProcessMessages(false), | ||||
pendingSyncFence(false), syncFence(false), fenceFlags(0), | pendingSyncFence(false), syncFence(false), fenceFlags(0), | ||||
fenceDataLen(0), fenceData(NULL), congestionTimer(this), | fenceDataLen(0), fenceData(NULL), congestionTimer(this), |
class VNCSConnectionST : private SConnection, | class VNCSConnectionST : private SConnection, | ||||
public Timer::Callback { | public Timer::Callback { | ||||
public: | public: | ||||
VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse); | |||||
VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse, | |||||
AccessRights ar); | |||||
virtual ~VNCSConnectionST(); | virtual ~VNCSConnectionST(); | ||||
// SConnection methods | // SConnection methods |
// outgoing is set to true if the socket was created by connecting out | // outgoing is set to true if the socket was created by connecting out | ||||
// to another host, or false if the socket was created by accept()ing | // to another host, or false if the socket was created by accept()ing | ||||
// an incoming connection. | // an incoming connection. | ||||
virtual void addSocket(network::Socket* sock, bool outgoing=false) = 0; | |||||
// accessRights allows to set the access rights to the server. | |||||
virtual void addSocket(network::Socket* sock, bool outgoing=false, | |||||
AccessRights accessRights = AccessDefault) = 0; | |||||
// removeSocket() tells the server to stop serving the Socket. The | // removeSocket() tells the server to stop serving the Socket. The | ||||
// caller retains ownership of the Socket - the server must NOT | // caller retains ownership of the Socket - the server must NOT |
// VNCServer methods | // VNCServer methods | ||||
void VNCServerST::addSocket(network::Socket* sock, bool outgoing) | |||||
void VNCServerST::addSocket(network::Socket* sock, bool outgoing, AccessRights accessRights) | |||||
{ | { | ||||
// - Check the connection isn't black-marked | // - Check the connection isn't black-marked | ||||
// *** do this in getSecurity instead? | // *** do this in getSecurity instead? | ||||
connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); | connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); | ||||
disconnectTimer.stop(); | disconnectTimer.stop(); | ||||
VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); | |||||
VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing, accessRights); | |||||
clients.push_front(client); | clients.push_front(client); | ||||
client->init(); | client->init(); | ||||
} | } |
// addSocket | // addSocket | ||||
// Causes the server to allocate an RFB-protocol management | // Causes the server to allocate an RFB-protocol management | ||||
// structure for the socket & initialise it. | // structure for the socket & initialise it. | ||||
virtual void addSocket(network::Socket* sock, bool outgoing=false); | |||||
virtual void addSocket(network::Socket* sock, bool outgoing=false, | |||||
AccessRights ar=AccessDefault); | |||||
// removeSocket | // removeSocket | ||||
// Clean up any resources associated with the Socket | // Clean up any resources associated with the Socket |
#include <rdr/OutStream.h> | #include <rdr/OutStream.h> | ||||
#include <rdr/FileInStream.h> | #include <rdr/FileInStream.h> | ||||
#include <rfb/AccessRights.h> | |||||
#include <rfb/PixelFormat.h> | #include <rfb/PixelFormat.h> | ||||
#include <rfb/CConnection.h> | #include <rfb/CConnection.h> | ||||
} | } | ||||
SConn::SConn() | SConn::SConn() | ||||
: SConnection(rfb::AccessDefault) | |||||
{ | { | ||||
out = new DummyOutStream; | out = new DummyOutStream; | ||||
setStreams(NULL, out); | setStreams(NULL, out); |
return True; | return True; | ||||
} | } | ||||
Bool XVncExtConnect(Display* dpy, const char* hostAndPort) | |||||
Bool XVncExtConnect(Display* dpy, const char* hostAndPort, Bool viewOnly) | |||||
{ | { | ||||
xVncExtConnectReq* req; | xVncExtConnectReq* req; | ||||
xVncExtConnectReply rep; | xVncExtConnectReply rep; | ||||
req->vncExtReqType = X_VncExtConnect; | req->vncExtReqType = X_VncExtConnect; | ||||
req->length += (strLen + 3) >> 2; | req->length += (strLen + 3) >> 2; | ||||
req->strLen = strLen; | req->strLen = strLen; | ||||
req->viewOnly = (CARD8)viewOnly; | |||||
Data(dpy, hostAndPort, strLen); | Data(dpy, hostAndPort, strLen); | ||||
if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) { | if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) { | ||||
UnlockDisplay(dpy); | UnlockDisplay(dpy); |
char** XVncExtListParams(Display* dpy, int* nParams); | char** XVncExtListParams(Display* dpy, int* nParams); | ||||
void XVncExtFreeParamList(char** list); | void XVncExtFreeParamList(char** list); | ||||
Bool XVncExtSelectInput(Display* dpy, Window w, int mask); | Bool XVncExtSelectInput(Display* dpy, Window w, int mask); | ||||
Bool XVncExtConnect(Display* dpy, const char* hostAndPort); | |||||
Bool XVncExtConnect(Display* dpy, const char* hostAndPort, Bool viewOnly); | |||||
Bool XVncExtGetQueryConnect(Display* dpy, char** addr, | Bool XVncExtGetQueryConnect(Display* dpy, char** addr, | ||||
char** user, int* timeout, void** opaqueId); | char** user, int* timeout, void** opaqueId); | ||||
Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve); | Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve); | ||||
CARD8 vncExtReqType; /* always VncExtConnect */ | CARD8 vncExtReqType; /* always VncExtConnect */ | ||||
CARD16 length B16; | CARD16 length B16; | ||||
CARD8 strLen; | CARD8 strLen; | ||||
CARD8 pad0; | |||||
CARD8 viewOnly; | |||||
CARD16 pad1 B16; | CARD16 pad1 B16; | ||||
} xVncExtConnectReq; | } xVncExtConnectReq; | ||||
#define sz_xVncExtConnectReq 8 | #define sz_xVncExtConnectReq 8 |
{ | { | ||||
fprintf(stderr,"usage: %s [parameters]\n", | fprintf(stderr,"usage: %s [parameters]\n", | ||||
programName); | programName); | ||||
fprintf(stderr," %s [parameters] -connect <host>[:<port>]\n", | |||||
programName); | |||||
fprintf(stderr," %s [parameters] -connect " | |||||
"[-view-only] <host>[:<port>]\n", programName); | |||||
fprintf(stderr," %s [parameters] -disconnect\n", programName); | fprintf(stderr," %s [parameters] -disconnect\n", programName); | ||||
fprintf(stderr," %s [parameters] [-set] <Xvnc-param>=<value> ...\n", | fprintf(stderr," %s [parameters] [-set] <Xvnc-param>=<value> ...\n", | ||||
programName); | programName); | ||||
if (i < argc) { | if (i < argc) { | ||||
for (; i < argc; i++) { | for (; i < argc; i++) { | ||||
if (strcmp(argv[i], "-connect") == 0) { | if (strcmp(argv[i], "-connect") == 0) { | ||||
Bool viewOnly = False; | |||||
i++; | i++; | ||||
if (strcmp(argv[i], "-view-only") == 0) { | |||||
viewOnly = True; | |||||
i++; | |||||
} | |||||
if (i >= argc) usage(); | if (i >= argc) usage(); | ||||
if (!XVncExtConnect(dpy, argv[i])) { | |||||
if (!XVncExtConnect(dpy, argv[i], viewOnly)) { | |||||
fprintf(stderr,"connecting to %s failed\n",argv[i]); | fprintf(stderr,"connecting to %s failed\n",argv[i]); | ||||
} | } | ||||
} else if (strcmp(argv[i], "-disconnect") == 0) { | } else if (strcmp(argv[i], "-disconnect") == 0) { | ||||
if (!XVncExtConnect(dpy, "")) { | |||||
if (!XVncExtConnect(dpy, "", False)) { | |||||
fprintf(stderr,"disconnecting all clients failed\n"); | fprintf(stderr,"disconnecting all clients failed\n"); | ||||
} | } | ||||
} else if (strcmp(argv[i], "-get") == 0) { | } else if (strcmp(argv[i], "-get") == 0) { |
.br | .br | ||||
.B vncconfig | .B vncconfig | ||||
.RI [ parameters ] | .RI [ parameters ] | ||||
.B \-connect | |||||
.B \-connect \fP[\fB-view-only\fP] | |||||
.IR host [: port ] | .IR host [: port ] | ||||
.br | .br | ||||
.B vncconfig | .B vncconfig | ||||
.SH OPTIONS | .SH OPTIONS | ||||
.TP | .TP | ||||
.B \-connect \fIhost\fP[:\fIport\fP] | |||||
.B \-connect \fP[\fB-view-only\fP] \fIhost\fP[:\fIport\fP] | |||||
Tells an Xvnc server to make a "reverse" connection to a listening VNC viewer | Tells an Xvnc server to make a "reverse" connection to a listening VNC viewer | ||||
(normally connections are made the other way round - the viewer connects to the | (normally connections are made the other way round - the viewer connects to the | ||||
server). \fIhost\fP is the host where the listening viewer is running. If it's | server). \fIhost\fP is the host where the listening viewer is running. If it's | ||||
not listening on the default port of 5500, you can specify \fIhost:port\fP | not listening on the default port of 5500, you can specify \fIhost:port\fP | ||||
instead. | |||||
instead. The \fB-view-only\fP option specifies that the server must ignore all | |||||
keyboard or mouse events sent by the client. | |||||
. | . | ||||
.TP | .TP | ||||
.B \-disconnect | .B \-disconnect |
} | } | ||||
} | } | ||||
void XserverDesktop::addClient(Socket* sock, bool reverse) | |||||
void XserverDesktop::addClient(Socket* sock, bool reverse, bool viewOnly) | |||||
{ | { | ||||
vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse); | vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse); | ||||
server->addSocket(sock, reverse); | |||||
server->addSocket(sock, reverse, viewOnly ? AccessView : AccessDefault); | |||||
vncSetNotifyFd(sock->getFd(), screenIndex, true, false); | vncSetNotifyFd(sock->getFd(), screenIndex, true, false); | ||||
} | } | ||||
void add_copied(const rfb::Region &dest, const rfb::Point &delta); | void add_copied(const rfb::Region &dest, const rfb::Point &delta); | ||||
void handleSocketEvent(int fd, bool read, bool write); | void handleSocketEvent(int fd, bool read, bool write); | ||||
void blockHandler(int* timeout); | void blockHandler(int* timeout); | ||||
void addClient(network::Socket* sock, bool reverse); | |||||
void addClient(network::Socket* sock, bool reverse, bool viewOnly); | |||||
void disconnectClients(); | void disconnectClients(); | ||||
// QueryConnect methods called from X server code | // QueryConnect methods called from X server code |
address[stuff->strLen] = 0; | address[stuff->strLen] = 0; | ||||
rep.success = 0; | rep.success = 0; | ||||
if (vncConnectClient(address) == 0) | |||||
if (vncConnectClient(address, (int)stuff->viewOnly) == 0) | |||||
rep.success = 1; | rep.success = 1; | ||||
rep.type = X_Reply; | rep.type = X_Reply; |
if (scr == 0 && vncInetdSock != -1 && listeners.empty()) { | if (scr == 0 && vncInetdSock != -1 && listeners.empty()) { | ||||
network::Socket* sock = new network::TcpSocket(vncInetdSock); | network::Socket* sock = new network::TcpSocket(vncInetdSock); | ||||
desktop[scr]->addClient(sock, false); | |||||
desktop[scr]->addClient(sock, false, false); | |||||
vlog.info("added inetd sock"); | vlog.info("added inetd sock"); | ||||
} | } | ||||
} | } | ||||
desktop[scr]->sendClipboardData(data); | desktop[scr]->sendClipboardData(data); | ||||
} | } | ||||
int vncConnectClient(const char *addr) | |||||
int vncConnectClient(const char *addr, int viewOnly) | |||||
{ | { | ||||
if (strlen(addr) == 0) { | if (strlen(addr) == 0) { | ||||
try { | try { | ||||
try { | try { | ||||
network::Socket* sock = new network::TcpSocket(host.c_str(), port); | network::Socket* sock = new network::TcpSocket(host.c_str(), port); | ||||
desktop[0]->addClient(sock, true); | |||||
vlog.info("Reverse connection: %s:%d%s", host.c_str(), port, | |||||
viewOnly ? " (view only)" : ""); | |||||
desktop[0]->addClient(sock, true, (bool)viewOnly); | |||||
} catch (rdr::Exception& e) { | } catch (rdr::Exception& e) { | ||||
vlog.error("Reverse connection: %s",e.str()); | vlog.error("Reverse connection: %s",e.str()); | ||||
return -1; | return -1; |
void vncAnnounceClipboard(int available); | void vncAnnounceClipboard(int available); | ||||
void vncSendClipboardData(const char* data); | void vncSendClipboardData(const char* data); | ||||
int vncConnectClient(const char *addr); | |||||
int vncConnectClient(const char *addr, int viewOnly); | |||||
void vncGetQueryConnect(uint32_t *opaqueId, const char**username, | void vncGetQueryConnect(uint32_t *opaqueId, const char**username, | ||||
const char **address, int *timeout); | const char **address, int *timeout); |