From 4bfc5c6eada6fa40ad68b4ebab1b950c6d6ea6a8 Mon Sep 17 00:00:00 2001 From: Carlos Santos Date: Wed, 29 Nov 2023 18:52:52 -0300 Subject: [PATCH] vncconfig: add option to force view-only remote client connections 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 --- common/rfb/SConnection.cxx | 4 ++-- common/rfb/SConnection.h | 5 ++++- common/rfb/SSecurity.cxx | 2 +- common/rfb/VNCSConnectionST.cxx | 5 +++-- common/rfb/VNCSConnectionST.h | 3 ++- common/rfb/VNCServer.h | 4 +++- common/rfb/VNCServerST.cxx | 4 ++-- common/rfb/VNCServerST.h | 3 ++- tests/perf/encperf.cxx | 3 +++ unix/vncconfig/vncExt.c | 3 ++- unix/vncconfig/vncExt.h | 4 ++-- unix/vncconfig/vncconfig.cxx | 14 ++++++++++---- unix/vncconfig/vncconfig.man | 7 ++++--- unix/xserver/hw/vnc/XserverDesktop.cc | 4 ++-- unix/xserver/hw/vnc/XserverDesktop.h | 2 +- unix/xserver/hw/vnc/vncExt.c | 2 +- unix/xserver/hw/vnc/vncExtInit.cc | 8 +++++--- unix/xserver/hw/vnc/vncExtInit.h | 2 +- 18 files changed, 50 insertions(+), 29 deletions(-) diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index 462c34c2e6..94a5b9d64e 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -43,12 +43,12 @@ using namespace rfb; static LogWriter vlog("SConnection"); -SConnection::SConnection() +SConnection::SConnection(AccessRights accessRights) : readyForSetColourMapEntries(false), is(0), os(0), reader_(0), writer_(0), ssecurity(0), authFailureTimer(this, &SConnection::handleAuthFailureTimeout), state_(RFBSTATE_UNINITIALISED), preferredEncoding(encodingRaw), - accessRights(AccessNone), hasRemoteClipboard(false), + accessRights(accessRights), hasRemoteClipboard(false), hasLocalClipboard(false), unsolicitedClipboardAttempt(false) { diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index fb8b0b4cb0..cc88cd1e64 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -43,7 +43,7 @@ namespace rfb { class SConnection : public SMsgHandler { public: - SConnection(); + SConnection(AccessRights accessRights); virtual ~SConnection(); // Methods to initialise the connection @@ -176,6 +176,9 @@ namespace rfb { // clipboard via handleClipboardRequest(). 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 // of a SConnection to the server. How the access rights are treated // is up to the derived class. diff --git a/common/rfb/SSecurity.cxx b/common/rfb/SSecurity.cxx index 1cc5c4dc09..109882d342 100644 --- a/common/rfb/SSecurity.cxx +++ b/common/rfb/SSecurity.cxx @@ -25,7 +25,7 @@ using namespace rfb; SSecurity::SSecurity(SConnection* sc) - : sc(sc), accessRights(AccessDefault) + : sc(sc), accessRights(sc->getAccessRights()) { } diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 906f5f66c1..f1194eb69a 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -51,8 +51,9 @@ static LogWriter vlog("VNCSConnST"); static Cursor emptyCursor(0, 0, Point(0, 0), NULL); 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), pendingSyncFence(false), syncFence(false), fenceFlags(0), fenceDataLen(0), fenceData(NULL), congestionTimer(this), diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index edc0391edc..2f117a7579 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -40,7 +40,8 @@ namespace rfb { class VNCSConnectionST : private SConnection, public Timer::Callback { public: - VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse); + VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse, + AccessRights ar); virtual ~VNCSConnectionST(); // SConnection methods diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 6d75f8f84f..cf14bd8621 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -39,7 +39,9 @@ namespace rfb { // 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 // 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 // caller retains ownership of the Socket - the server must NOT diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index edf23a4f21..fb4210680c 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -132,7 +132,7 @@ VNCServerST::~VNCServerST() // 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 // *** do this in getSecurity instead? @@ -163,7 +163,7 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing) connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); disconnectTimer.stop(); - VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing); + VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing, accessRights); clients.push_front(client); client->init(); } diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 4a8efa465c..d303831e8d 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -56,7 +56,8 @@ namespace rfb { // addSocket // Causes the server to allocate an RFB-protocol management // 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 // Clean up any resources associated with the Socket diff --git a/tests/perf/encperf.cxx b/tests/perf/encperf.cxx index 55fa386eb1..388dcc9559 100644 --- a/tests/perf/encperf.cxx +++ b/tests/perf/encperf.cxx @@ -41,6 +41,8 @@ #include #include +#include + #include #include @@ -303,6 +305,7 @@ void Manager::getStats(double& ratio, unsigned long long& encodedBytes, } SConn::SConn() +: SConnection(rfb::AccessDefault) { out = new DummyOutStream; setStreams(NULL, out); diff --git a/unix/vncconfig/vncExt.c b/unix/vncconfig/vncExt.c index f19123b889..4ec671b885 100644 --- a/unix/vncconfig/vncExt.c +++ b/unix/vncconfig/vncExt.c @@ -228,7 +228,7 @@ Bool XVncExtSelectInput(Display* dpy, Window w, int mask) return True; } -Bool XVncExtConnect(Display* dpy, const char* hostAndPort) +Bool XVncExtConnect(Display* dpy, const char* hostAndPort, Bool viewOnly) { xVncExtConnectReq* req; xVncExtConnectReply rep; @@ -243,6 +243,7 @@ Bool XVncExtConnect(Display* dpy, const char* hostAndPort) req->vncExtReqType = X_VncExtConnect; req->length += (strLen + 3) >> 2; req->strLen = strLen; + req->viewOnly = (CARD8)viewOnly; Data(dpy, hostAndPort, strLen); if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) { UnlockDisplay(dpy); diff --git a/unix/vncconfig/vncExt.h b/unix/vncconfig/vncExt.h index 2b24469e34..4383248c9f 100644 --- a/unix/vncconfig/vncExt.h +++ b/unix/vncconfig/vncExt.h @@ -46,7 +46,7 @@ char* XVncExtGetParamDesc(Display* dpy, const char* param); char** XVncExtListParams(Display* dpy, int* nParams); void XVncExtFreeParamList(char** list); 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, char** user, int* timeout, void** opaqueId); Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve); @@ -181,7 +181,7 @@ typedef struct { CARD8 vncExtReqType; /* always VncExtConnect */ CARD16 length B16; CARD8 strLen; - CARD8 pad0; + CARD8 viewOnly; CARD16 pad1 B16; } xVncExtConnectReq; #define sz_xVncExtConnectReq 8 diff --git a/unix/vncconfig/vncconfig.cxx b/unix/vncconfig/vncconfig.cxx index f39e9934ae..27f018d10f 100644 --- a/unix/vncconfig/vncconfig.cxx +++ b/unix/vncconfig/vncconfig.cxx @@ -177,8 +177,8 @@ static void usage() { fprintf(stderr,"usage: %s [parameters]\n", programName); - fprintf(stderr," %s [parameters] -connect [:]\n", - programName); + fprintf(stderr," %s [parameters] -connect " + "[-viewOnly|-view-only] [:]\n", programName); fprintf(stderr," %s [parameters] -disconnect\n", programName); fprintf(stderr," %s [parameters] [-set] = ...\n", programName); @@ -240,13 +240,19 @@ int main(int argc, char** argv) if (i < argc) { for (; i < argc; i++) { if (strcmp(argv[i], "-connect") == 0) { + Bool viewOnly = False; i++; + if (strcmp(argv[i], "-viewOnly") == 0 || + strcmp(argv[i], "-view-only") == 0) { + viewOnly = True; + i++; + } if (i >= argc) usage(); - if (!XVncExtConnect(dpy, argv[i])) { + if (!XVncExtConnect(dpy, argv[i], viewOnly)) { fprintf(stderr,"connecting to %s failed\n",argv[i]); } } else if (strcmp(argv[i], "-disconnect") == 0) { - if (!XVncExtConnect(dpy, "")) { + if (!XVncExtConnect(dpy, "", False)) { fprintf(stderr,"disconnecting all clients failed\n"); } } else if (strcmp(argv[i], "-get") == 0) { diff --git a/unix/vncconfig/vncconfig.man b/unix/vncconfig/vncconfig.man index ed9ddda4b1..b47787fac1 100644 --- a/unix/vncconfig/vncconfig.man +++ b/unix/vncconfig/vncconfig.man @@ -7,7 +7,7 @@ vncconfig \- configure and control a VNC server .br .B vncconfig .RI [ parameters ] -.B \-connect +.B \-connect \fP[\fB-viewOnly\fP|\fB-view-only\fP] .IR host [: port ] .br .B vncconfig @@ -55,12 +55,13 @@ is no VNC extension. .SH OPTIONS .TP -.B \-connect \fIhost\fP[:\fIport\fP] +.B \-connect \fP[\fB-viewOnly\fP|\fB-view-only\fP] \fIhost\fP[:\fIport\fP] 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 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 -instead. +instead. The \fB-viewOnly\fP (or \fB-view-Only\fP) option specifies that +the server must ignore all keyboard or mouse events sent by the client. . .TP .B \-disconnect diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index a2160a7214..4cf37937ba 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -402,10 +402,10 @@ void XserverDesktop::blockHandler(int* timeout) } } -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); - server->addSocket(sock, reverse); + server->addSocket(sock, reverse, viewOnly ? AccessView : AccessDefault); vncSetNotifyFd(sock->getFd(), screenIndex, true, false); } diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index a42b5440f1..9cc5bf792e 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -72,7 +72,7 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, void add_copied(const rfb::Region &dest, const rfb::Point &delta); void handleSocketEvent(int fd, bool read, bool write); void blockHandler(int* timeout); - void addClient(network::Socket* sock, bool reverse); + void addClient(network::Socket* sock, bool reverse, bool viewOnly); void disconnectClients(); // QueryConnect methods called from X server code diff --git a/unix/xserver/hw/vnc/vncExt.c b/unix/xserver/hw/vnc/vncExt.c index 89c105503f..e98275c2c8 100644 --- a/unix/xserver/hw/vnc/vncExt.c +++ b/unix/xserver/hw/vnc/vncExt.c @@ -348,7 +348,7 @@ static int ProcVncExtConnect(ClientPtr client) address[stuff->strLen] = 0; rep.success = 0; - if (vncConnectClient(address) == 0) + if (vncConnectClient(address, (int)stuff->viewOnly) == 0) rep.success = 1; rep.type = X_Reply; diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 1dfe76d741..b260e62664 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -267,7 +267,7 @@ void vncExtensionInit(void) if (scr == 0 && vncInetdSock != -1 && listeners.empty()) { network::Socket* sock = new network::TcpSocket(vncInetdSock); - desktop[scr]->addClient(sock, false); + desktop[scr]->addClient(sock, false, false); vlog.info("added inetd sock"); } } @@ -343,7 +343,7 @@ void vncSendClipboardData(const char* data) desktop[scr]->sendClipboardData(data); } -int vncConnectClient(const char *addr) +int vncConnectClient(const char *addr, int viewOnly) { if (strlen(addr) == 0) { try { @@ -362,7 +362,9 @@ int vncConnectClient(const char *addr) try { 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) { vlog.error("Reverse connection: %s",e.str()); return -1; diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index c317d8a216..333e32a90d 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -57,7 +57,7 @@ void vncRequestClipboard(void); void vncAnnounceClipboard(int available); 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, const char **address, int *timeout);