import CS tigervnc-1.14.1-4.el9

This commit is contained in:
eabdullin 2025-03-11 08:15:47 +00:00
parent 3fc1ab94e6
commit 40b2d98423
12 changed files with 981 additions and 126 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/tigervnc-1.14.0.tar.gz
SOURCES/tigervnc-1.14.1.tar.gz

View File

@ -1 +1 @@
9e67944113159da85f42c24b43f40b842f23feb3 SOURCES/tigervnc-1.14.0.tar.gz
bc3c8bc9f454eb307011cd5965251f4a28040a25 SOURCES/tigervnc-1.14.1.tar.gz

View File

@ -0,0 +1,543 @@
From c23be952f50ba34c49134b6280ce503f154dc9bc Mon Sep 17 00:00:00 2001
From: Gaurav Ujjwal <gujjwal00@gmail.com>
Date: Wed, 25 Sep 2024 21:21:26 +0530
Subject: [PATCH] Add clipboard support to x0vncserver
---
unix/tx/TXWindow.cxx | 13 ++-
unix/tx/TXWindow.h | 3 +-
unix/x0vncserver/CMakeLists.txt | 1 +
unix/x0vncserver/XDesktop.cxx | 49 +++++++-
unix/x0vncserver/XDesktop.h | 13 ++-
unix/x0vncserver/XSelection.cxx | 195 +++++++++++++++++++++++++++++++
unix/x0vncserver/XSelection.h | 58 +++++++++
unix/x0vncserver/x0vncserver.cxx | 5 -
unix/x0vncserver/x0vncserver.man | 21 ++++
9 files changed, 344 insertions(+), 14 deletions(-)
create mode 100644 unix/x0vncserver/XSelection.cxx
create mode 100644 unix/x0vncserver/XSelection.h
diff --git a/unix/tx/TXWindow.cxx b/unix/tx/TXWindow.cxx
index ee097e4..b10ed84 100644
--- a/unix/tx/TXWindow.cxx
+++ b/unix/tx/TXWindow.cxx
@@ -36,7 +36,7 @@ std::list<TXWindow*> windows;
Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
-Atom xaCLIPBOARD;
+Atom xaCLIPBOARD, xaUTF8_STRING, xaINCR;
unsigned long TXWindow::black, TXWindow::white;
unsigned long TXWindow::defaultFg, TXWindow::defaultBg;
unsigned long TXWindow::lightBg, TXWindow::darkBg;
@@ -65,6 +65,8 @@ void TXWindow::init(Display* dpy, const char* defaultWindowClass_)
xaSELECTION_TIME = XInternAtom(dpy, "SELECTION_TIME", False);
xaSELECTION_STRING = XInternAtom(dpy, "SELECTION_STRING", False);
xaCLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False);
+ xaUTF8_STRING = XInternAtom(dpy, "UTF8_STRING", False);
+ xaINCR = XInternAtom(dpy, "INCR", False);
XColor cols[6];
cols[0].red = cols[0].green = cols[0].blue = 0x0000;
cols[1].red = cols[1].green = cols[1].blue = 0xbbbb;
@@ -462,17 +464,18 @@ void TXWindow::handleXEvent(XEvent* ev)
} else {
se.property = ev->xselectionrequest.property;
if (se.target == xaTARGETS) {
- Atom targets[2];
+ Atom targets[3];
targets[0] = xaTIMESTAMP;
targets[1] = XA_STRING;
+ targets[2] = xaUTF8_STRING;
XChangeProperty(dpy, se.requestor, se.property, XA_ATOM, 32,
- PropModeReplace, (unsigned char*)targets, 2);
+ PropModeReplace, (unsigned char*)targets, 3);
} else if (se.target == xaTIMESTAMP) {
Time t = selectionOwnTime[se.selection];
XChangeProperty(dpy, se.requestor, se.property, XA_INTEGER, 32,
PropModeReplace, (unsigned char*)&t, 1);
- } else if (se.target == XA_STRING) {
- if (!selectionRequest(se.requestor, se.selection, se.property))
+ } else if (se.target == XA_STRING || se.target == xaUTF8_STRING) {
+ if (!selectionRequest(se.requestor, se.selection, se.target, se.property))
se.property = None;
} else {
se.property = None;
diff --git a/unix/tx/TXWindow.h b/unix/tx/TXWindow.h
index 223c07a..32ae9a3 100644
--- a/unix/tx/TXWindow.h
+++ b/unix/tx/TXWindow.h
@@ -155,6 +155,7 @@ public:
// returning true if successful, false otherwise.
virtual bool selectionRequest(Window /*requestor*/,
Atom /*selection*/,
+ Atom /*target*/,
Atom /*property*/) { return false;}
// Static methods
@@ -224,6 +225,6 @@ private:
extern Atom wmProtocols, wmDeleteWindow, wmTakeFocus;
extern Atom xaTIMESTAMP, xaTARGETS, xaSELECTION_TIME, xaSELECTION_STRING;
-extern Atom xaCLIPBOARD;
+extern Atom xaCLIPBOARD, xaUTF8_STRING, xaINCR;
#endif
diff --git a/unix/x0vncserver/CMakeLists.txt b/unix/x0vncserver/CMakeLists.txt
index 5ce9577..9d6d213 100644
--- a/unix/x0vncserver/CMakeLists.txt
+++ b/unix/x0vncserver/CMakeLists.txt
@@ -11,6 +11,7 @@ add_executable(x0vncserver
XPixelBuffer.cxx
XDesktop.cxx
RandrGlue.c
+ XSelection.cxx
../vncconfig/QueryConnectDialog.cxx
)
diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx
index 1e52987..db5b6ae 100644
--- a/unix/x0vncserver/XDesktop.cxx
+++ b/unix/x0vncserver/XDesktop.cxx
@@ -43,6 +43,7 @@
#endif
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
+#include <X11/Xatom.h>
#endif
#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
@@ -81,7 +82,7 @@ static const char * ledNames[XDESKTOP_N_LEDS] = {
XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
: dpy(dpy_), geometry(geometry_), pb(0), server(0),
- queryConnectDialog(0), queryConnectSock(0),
+ queryConnectDialog(0), queryConnectSock(0), selection(dpy_, this),
oldButtonMask(0), haveXtest(false), haveDamage(false),
maxButtons(0), running(false), ledMasks(), ledState(0),
codeMap(0), codeMapLen(0)
@@ -179,10 +180,15 @@ XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) {
XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy),
XFixesDisplayCursorNotifyMask);
+
+ XFixesSelectSelectionInput(dpy, DefaultRootWindow(dpy), XA_PRIMARY,
+ XFixesSetSelectionOwnerNotifyMask);
+ XFixesSelectSelectionInput(dpy, DefaultRootWindow(dpy), xaCLIPBOARD,
+ XFixesSetSelectionOwnerNotifyMask);
} else {
#endif
vlog.info("XFIXES extension not present");
- vlog.info("Will not be able to display cursors");
+ vlog.info("Will not be able to display cursors or monitor clipboard");
#ifdef HAVE_XFIXES
}
#endif
@@ -892,6 +898,20 @@ bool XDesktop::handleGlobalEvent(XEvent* ev) {
return false;
return setCursor();
+ }
+ else if (ev->type == xfixesEventBase + XFixesSelectionNotify) {
+ XFixesSelectionNotifyEvent* sev = (XFixesSelectionNotifyEvent*)ev;
+
+ if (!running)
+ return true;
+
+ if (sev->subtype != XFixesSetSelectionOwnerNotify)
+ return false;
+
+ selection.handleSelectionOwnerChange(sev->owner, sev->selection,
+ sev->timestamp);
+
+ return true;
#endif
#ifdef HAVE_XRANDR
} else if (ev->type == Expose) {
@@ -1039,3 +1059,28 @@ bool XDesktop::setCursor()
return true;
}
#endif
+
+// X selection availability changed, let VNC clients know
+void XDesktop::handleXSelectionAnnounce(bool available) {
+ server->announceClipboard(available);
+}
+
+// A VNC client wants data, send request to selection owner
+void XDesktop::handleClipboardRequest() {
+ selection.requestSelectionData();
+}
+
+// Data is available, send it to clients
+void XDesktop::handleXSelectionData(const char* data) {
+ server->sendClipboardData(data);
+}
+
+// When a client says it has clipboard data, request it
+void XDesktop::handleClipboardAnnounce(bool available) {
+ if(available) server->requestClipboard();
+}
+
+// Client has sent the data
+void XDesktop::handleClipboardData(const char* data) {
+ if (data) selection.handleClientClipboardData(data);
+}
diff --git a/unix/x0vncserver/XDesktop.h b/unix/x0vncserver/XDesktop.h
index 4777a65..bc8d2a9 100644
--- a/unix/x0vncserver/XDesktop.h
+++ b/unix/x0vncserver/XDesktop.h
@@ -32,6 +32,8 @@
#include <vncconfig/QueryConnectDialog.h>
+#include "XSelection.h"
+
class Geometry;
class XPixelBuffer;
@@ -46,7 +48,8 @@ struct AddedKeySym
class XDesktop : public rfb::SDesktop,
public TXGlobalEventHandler,
- public QueryResultCallback
+ public QueryResultCallback,
+ public XSelectionHandler
{
public:
XDesktop(Display* dpy_, Geometry *geometry);
@@ -65,6 +68,13 @@ public:
virtual void clientCutText(const char* str);
virtual unsigned int setScreenLayout(int fb_width, int fb_height,
const rfb::ScreenSet& layout);
+ void handleClipboardRequest() override;
+ void handleClipboardAnnounce(bool available) override;
+ void handleClipboardData(const char* data) override;
+
+ // -=- XSelectionHandler interface
+ void handleXSelectionAnnounce(bool available) override;
+ void handleXSelectionData(const char* data) override;
// -=- TXGlobalEventHandler interface
virtual bool handleGlobalEvent(XEvent* ev);
@@ -80,6 +90,7 @@ protected:
rfb::VNCServer* server;
QueryConnectDialog* queryConnectDialog;
network::Socket* queryConnectSock;
+ XSelection selection;
int oldButtonMask;
bool haveXtest;
bool haveDamage;
diff --git a/unix/x0vncserver/XSelection.cxx b/unix/x0vncserver/XSelection.cxx
new file mode 100644
index 0000000..72dd537
--- /dev/null
+++ b/unix/x0vncserver/XSelection.cxx
@@ -0,0 +1,195 @@
+/* Copyright (C) 2024 Gaurav Ujjwal. All Rights Reserved.
+ *
+ * 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.
+ */
+
+#include <X11/Xatom.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <x0vncserver/XSelection.h>
+
+rfb::BoolParameter setPrimary("SetPrimary",
+ "Set the PRIMARY as well as the CLIPBOARD selection",
+ true);
+rfb::BoolParameter sendPrimary("SendPrimary",
+ "Send the PRIMARY as well as the CLIPBOARD selection",
+ true);
+
+static rfb::LogWriter vlog("XSelection");
+
+XSelection::XSelection(Display* dpy_, XSelectionHandler* handler_)
+ : TXWindow(dpy_, 1, 1, nullptr), handler(handler_), announcedSelection(None)
+{
+ probeProperty = XInternAtom(dpy, "TigerVNC_ProbeProperty", False);
+ transferProperty = XInternAtom(dpy, "TigerVNC_TransferProperty", False);
+ timestampProperty = XInternAtom(dpy, "TigerVNC_TimestampProperty", False);
+ setName("TigerVNC Clipboard (x0vncserver)");
+ addEventMask(PropertyChangeMask); // Required for PropertyNotify events
+}
+
+static Bool PropertyEventMatcher(Display* /* dpy */, XEvent* ev, XPointer prop)
+{
+ if (ev->type == PropertyNotify && ev->xproperty.atom == *((Atom*)prop))
+ return True;
+ else
+ return False;
+}
+
+Time XSelection::getXServerTime()
+{
+ XEvent ev;
+ uint8_t data = 0;
+
+ // Trigger a PropertyNotify event to extract server time
+ XChangeProperty(dpy, win(), timestampProperty, XA_STRING, 8, PropModeReplace,
+ &data, sizeof(data));
+ XIfEvent(dpy, &ev, &PropertyEventMatcher, (XPointer)&timestampProperty);
+ return ev.xproperty.time;
+}
+
+// Takes ownership of selections, backed by given data.
+void XSelection::handleClientClipboardData(const char* data)
+{
+ vlog.debug("Received client clipboard data, taking selection ownership");
+
+ Time time = getXServerTime();
+ ownSelection(xaCLIPBOARD, time);
+ if (!selectionOwner(xaCLIPBOARD))
+ vlog.error("Unable to own CLIPBOARD selection");
+
+ if (setPrimary) {
+ ownSelection(XA_PRIMARY, time);
+ if (!selectionOwner(XA_PRIMARY))
+ vlog.error("Unable to own PRIMARY selection");
+ }
+
+ if (selectionOwner(xaCLIPBOARD) || selectionOwner(XA_PRIMARY))
+ clientData = data;
+}
+
+// We own the selection and another X app has asked for data
+bool XSelection::selectionRequest(Window requestor, Atom selection, Atom target,
+ Atom property)
+{
+ if (clientData.empty() || requestor == win() || !selectionOwner(selection))
+ return false;
+
+ if (target == XA_STRING) {
+ std::string latin1 = rfb::utf8ToLatin1(clientData.data(), clientData.length());
+ XChangeProperty(dpy, requestor, property, XA_STRING, 8, PropModeReplace,
+ (unsigned char*)latin1.data(), latin1.length());
+ return true;
+ }
+
+ if (target == xaUTF8_STRING) {
+ XChangeProperty(dpy, requestor, property, xaUTF8_STRING, 8, PropModeReplace,
+ (unsigned char*)clientData.data(), clientData.length());
+ return true;
+ }
+
+ return false;
+}
+
+// Selection-owner change implies a change in selection data.
+void XSelection::handleSelectionOwnerChange(Window owner, Atom selection, Time time)
+{
+ if (selection != XA_PRIMARY && selection != xaCLIPBOARD)
+ return;
+ if (selection == XA_PRIMARY && !sendPrimary)
+ return;
+
+ if (selection == announcedSelection)
+ announceSelection(None);
+
+ if (owner == None || owner == win())
+ return;
+
+ if (!selectionOwner(XA_PRIMARY) && !selectionOwner(xaCLIPBOARD))
+ clientData = "";
+
+ XConvertSelection(dpy, selection, xaTARGETS, probeProperty, win(), time);
+}
+
+void XSelection::announceSelection(Atom selection)
+{
+ announcedSelection = selection;
+ handler->handleXSelectionAnnounce(selection != None);
+}
+
+void XSelection::requestSelectionData()
+{
+ if (announcedSelection != None)
+ XConvertSelection(dpy, announcedSelection, xaTARGETS, transferProperty, win(),
+ CurrentTime);
+}
+
+// Some information about selection is received from current owner
+void XSelection::selectionNotify(XSelectionEvent* ev, Atom type, int format,
+ int nitems, void* data)
+{
+ if (!ev || !data || type == None)
+ return;
+
+ if (ev->target == xaTARGETS) {
+ if (format != 32 || type != XA_ATOM)
+ return;
+
+ Atom* targets = (Atom*)data;
+ bool utf8Supported = false;
+ bool stringSupported = false;
+
+ for (int i = 0; i < nitems; i++) {
+ if (targets[i] == xaUTF8_STRING)
+ utf8Supported = true;
+ else if (targets[i] == XA_STRING)
+ stringSupported = true;
+ }
+
+ if (ev->property == probeProperty) {
+ // Only probing for now, will issue real request when client asks for data
+ if (stringSupported || utf8Supported)
+ announceSelection(ev->selection);
+ return;
+ }
+
+ // Prefer UTF-8 if available
+ if (utf8Supported)
+ XConvertSelection(dpy, ev->selection, xaUTF8_STRING, transferProperty, win(),
+ ev->time);
+ else if (stringSupported)
+ XConvertSelection(dpy, ev->selection, XA_STRING, transferProperty, win(),
+ ev->time);
+ } else if (ev->target == xaUTF8_STRING || ev->target == XA_STRING) {
+ if (type == xaINCR) {
+ // Incremental transfer is not supported
+ vlog.debug("Selected data is too big!");
+ return;
+ }
+
+ if (format != 8)
+ return;
+
+ if (type == xaUTF8_STRING) {
+ std::string result = rfb::convertLF((char*)data, nitems);
+ handler->handleXSelectionData(result.c_str());
+ } else if (type == XA_STRING) {
+ std::string result = rfb::convertLF((char*)data, nitems);
+ result = rfb::latin1ToUTF8(result.data(), result.length());
+ handler->handleXSelectionData(result.c_str());
+ }
+ }
+}
\ No newline at end of file
diff --git a/unix/x0vncserver/XSelection.h b/unix/x0vncserver/XSelection.h
new file mode 100644
index 0000000..fbe1f29
--- /dev/null
+++ b/unix/x0vncserver/XSelection.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2024 Gaurav Ujjwal. All Rights Reserved.
+ *
+ * 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 __XSELECTION_H__
+#define __XSELECTION_H__
+
+#include <string>
+#include <tx/TXWindow.h>
+
+class XSelectionHandler
+{
+public:
+ virtual void handleXSelectionAnnounce(bool available) = 0;
+ virtual void handleXSelectionData(const char* data) = 0;
+};
+
+class XSelection : TXWindow
+{
+public:
+ XSelection(Display* dpy_, XSelectionHandler* handler_);
+
+ void handleSelectionOwnerChange(Window owner, Atom selection, Time time);
+ void requestSelectionData();
+ void handleClientClipboardData(const char* data);
+
+private:
+ XSelectionHandler* handler;
+ Atom probeProperty;
+ Atom transferProperty;
+ Atom timestampProperty;
+ Atom announcedSelection;
+ std::string clientData; // Always in UTF-8
+
+ Time getXServerTime();
+ void announceSelection(Atom selection);
+
+ bool selectionRequest(Window requestor, Atom selection, Atom target,
+ Atom property) override;
+ void selectionNotify(XSelectionEvent* ev, Atom type, int format, int nitems,
+ void* data) override;
+};
+
+#endif
diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx
index d2999e2..b31450b 100644
--- a/unix/x0vncserver/x0vncserver.cxx
+++ b/unix/x0vncserver/x0vncserver.cxx
@@ -281,11 +281,6 @@ int main(int argc, char** argv)
Configuration::enableServerParams();
- // FIXME: We don't support clipboard yet
- Configuration::removeParam("AcceptCutText");
- Configuration::removeParam("SendCutText");
- Configuration::removeParam("MaxCutText");
-
// Assume different defaults when socket activated
if (hasSystemdListeners())
rfbport.setParam(-1);
diff --git a/unix/x0vncserver/x0vncserver.man b/unix/x0vncserver/x0vncserver.man
index 347e50e..5bc8807 100644
--- a/unix/x0vncserver/x0vncserver.man
+++ b/unix/x0vncserver/x0vncserver.man
@@ -222,6 +222,27 @@ Accept pointer movement and button events from clients. Default is on.
Accept requests to resize the size of the desktop. Default is on.
.
.TP
+.B \-AcceptCutText
+Accept clipboard updates from clients. Default is on.
+.
+.TP
+.B \-SetPrimary
+Set the PRIMARY as well as the CLIPBOARD selection. Default is on.
+.
+.TP
+.B \-MaxCutText \fIbytes\fP
+The maximum permitted size of an incoming clipboard update.
+Default is \fB262144\fP.
+.
+.TP
+.B \-SendCutText
+Send clipboard changes to clients. Default is on.
+.
+.TP
+.B \-SendPrimary
+Send the PRIMARY as well as the CLIPBOARD selection to clients. Default is on.
+.
+.TP
.B \-RemapKeys \fImapping
Sets up a keyboard mapping.
.I mapping

View File

@ -1,24 +0,0 @@
From 4f6a3521874da5a67fd746389cfa9b6199eb3582 Mon Sep 17 00:00:00 2001
From: Pierre Ossman <ossman@cendio.se>
Date: Mon, 29 Jul 2024 16:16:08 +0200
Subject: [PATCH] Add missing comma in default security type list
Otherwise it merges with the next entry, removing both of them from the
default list.
---
common/rfb/SecurityClient.cxx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/common/rfb/SecurityClient.cxx b/common/rfb/SecurityClient.cxx
index 12860662f..63e0cadc0 100644
--- a/common/rfb/SecurityClient.cxx
+++ b/common/rfb/SecurityClient.cxx
@@ -60,7 +60,7 @@ StringParameter SecurityClient::secTypes
"X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,"
#endif
#ifdef HAVE_NETTLE
- "RA2,RA2_256,RA2ne,RA2ne_256,DH,MSLogonII"
+ "RA2,RA2_256,RA2ne,RA2ne_256,DH,MSLogonII,"
#endif
"VncAuth,None",
ConfViewer);

View File

@ -0,0 +1,238 @@
From 8ac9bf0c061666d89d345a3d7149e1ef9c771655 Mon Sep 17 00:00:00 2001
From: Jan Grulich <jgrulich@redhat.com>
Date: Mon, 29 Jul 2024 14:31:14 +0200
Subject: [PATCH] Add option allowing to connect only the user owning the
running session
Checks, whether the user who is trying to authenticate is already logged
into the running session in order to allow or reject the connection.
This is expected to be used with 'plain' security type in combination
with 'PlainUsers=*' option allowing everyone to connect to the session.
---
common/rfb/VNCServerST.cxx | 7 --
unix/xserver/hw/vnc/XserverDesktop.cc | 120 +++++++++++++++++++++++++-
unix/xserver/hw/vnc/XserverDesktop.h | 7 ++
3 files changed, 126 insertions(+), 8 deletions(-)
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index 3831812..736a563 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -696,13 +696,6 @@ void VNCServerST::queryConnection(VNCSConnectionST* client,
return;
}
- // - Are we configured to do queries?
- if (!rfb::Server::queryConnect &&
- !client->getSock()->requiresQuery()) {
- approveConnection(client->getSock(), true, NULL);
- return;
- }
-
// - Does the client have the right to bypass the query?
if (client->accessCheck(AccessNoQuery))
{
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index d4ee16b..fe86d36 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -52,6 +52,11 @@
#include "XorgGlue.h"
#include "vncInput.h"
+#if HAVE_SYSTEMD_DAEMON
+# include <pwd.h>
+# include <systemd/sd-login.h>
+#endif
+
extern "C" {
void vncSetGlueContext(int screenIndex);
void vncPresentMscEvent(uint64_t id, uint64_t msc);
@@ -71,7 +76,15 @@ IntParameter queryConnectTimeout("QueryConnectTimeout",
"Accept Connection dialog before "
"rejecting the connection",
10);
-
+#ifdef HAVE_SYSTEMD_DAEMON
+BoolParameter approveLoggedUserOnly
+("ApproveLoggedUserOnly",
+ "Approve only the user who is currently logged into the session."
+ "This is expected to be combined with 'plain' security type and with "
+ "'PlainUsers=*' option allowing everyone to connect to the session."
+ "Default is off.",
+ false);
+#endif
XserverDesktop::XserverDesktop(int screenIndex_,
std::list<network::SocketListener*> listeners_,
@@ -168,11 +181,134 @@ void XserverDesktop::init(rfb::VNCServer* vs)
// ready state
}
+#ifdef HAVE_SYSTEMD_DAEMON
+bool XserverDesktop::checkUserLogged(const char* userName)
+{
+ bool ret = false;
+ bool noUserSession = true;
+ int res;
+ char **sessions;
+
+ res = sd_get_sessions(&sessions);
+ if (res < 0) {
+ vlog.debug("logind: failed to get sessions");
+ return false;
+ }
+
+ if (sessions != nullptr && sessions[0] != nullptr) {
+ for (int i = 0; sessions[i]; i++) {
+ uid_t uid;
+ char *clazz;
+ char *display;
+ char *type;
+ char *state;
+
+ res = sd_session_get_type(sessions[i], &type);
+ if (res < 0) {
+ vlog.debug("logind: failed to determine session type");
+ break;
+ }
+
+ if (strcmp(type, "x11") != 0) {
+ free(type);
+ continue;
+ }
+ free(type);
+
+ res = sd_session_get_display(sessions[i], &display);
+ if (res < 0) {
+ vlog.debug("logind: failed to determine display of session");
+ break;
+ }
+
+ std::string serverDisplay = ":" + std::to_string(screenIndex);
+ std::string serverDisplayIPv4 = "127.0.0.1:" + std::to_string(screenIndex);
+ std::string serverDisplayIPv6 = "::1:" + std::to_string(screenIndex);
+ if ((strcmp(display, serverDisplay.c_str()) != 0) &&
+ (strcmp(display, serverDisplayIPv4.c_str()) != 0) &&
+ (strcmp(display, serverDisplayIPv6.c_str()) != 0)) {
+ free(display);
+ continue;
+ }
+ free(display);
+
+ res = sd_session_get_class(sessions[i], &clazz);
+ if (res < 0) {
+ vlog.debug("logind: failed to determine session class");
+ break;
+ }
+
+ res = sd_session_get_state(sessions[i], &state);
+ if (res < 0) {
+ vlog.debug("logind: failed to determine session state");
+ break;
+ }
+
+ if (strcmp(state, "closing") == 0) {
+ free(state);
+ continue;
+ }
+ free(state);
+
+ res = sd_session_get_uid(sessions[i], &uid);
+ if (res < 0) {
+ vlog.debug("logind: failed to determine user id of session");
+ break;
+ }
+
+ if (uid != 0 && strcmp(clazz, "user") == 0) {
+ noUserSession = false;
+ }
+ free(clazz);
+
+ struct passwd *pw = getpwnam(userName);
+ if (!pw) {
+ vlog.debug("logind: user not found");
+ break;
+ }
+
+ if (uid == pw->pw_uid) {
+ ret = true;
+ break;
+ }
+ }
+ }
+
+ if (sessions) {
+ for (int i = 0; sessions[i]; i ++) {
+ free(sessions[i]);
+ }
+
+ free (sessions);
+ }
+
+ // If we didn't find a matching user, we can still allow the user
+ // to log in if there is no user session yet.
+ return !ret ? noUserSession : ret;
+}
+#endif
+
void XserverDesktop::queryConnection(network::Socket* sock,
const char* userName)
{
int count;
+#ifdef HAVE_SYSTEMD_DAEMON
+ // - Only owner of the session can be approved
+ if (approveLoggedUserOnly && !checkUserLogged(userName)) {
+ server->approveConnection(sock, false,
+ "The user is not owner of the running session");
+ return;
+ }
+#endif
+
+ // - Are we configured to do queries?
+ if (!rfb::Server::queryConnect &&
+ !sock->requiresQuery()) {
+ server->approveConnection(sock, true, nullptr);
+ return;
+ }
+
if (queryConnectTimer.isStarted()) {
server->approveConnection(sock, false, "Another connection is currently being queried.");
return;
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index e604295..aed188e 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -108,6 +108,13 @@ public:
virtual void grabRegion(const rfb::Region& r);
protected:
+#ifdef HAVE_SYSTEMD_DAEMON
+ // - Check whether user is logged into a session
+ // Returns true if user is already logged or there is no
+ // user session at all.
+ bool checkUserLogged(const char* userName);
+#endif
+
bool handleListenerEvent(int fd,
std::list<network::SocketListener*>* sockets,
rfb::VNCServer* sockserv);
diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man
index b9c429f..e4822f6 100644
--- a/unix/xserver/hw/vnc/Xvnc.man
+++ b/unix/xserver/hw/vnc/Xvnc.man
@@ -204,6 +204,13 @@ to allow any user to authenticate using this security type. Specify \fB%u\fP
to allow the user of the server process. Default is to deny all users.
.
.TP
+.B \-ApproveLoggedUserOnly
+Approve only the user who is currently logged into the session.
+This is expected to be combined with "Plain" security type and with
+"PlainUsers=*" option allowing everyone to connect to the session.
+Default is off.
+.
+.TP
.B \-pam_service \fIname\fP, \-PAMService \fIname\fP
PAM service name to use when authentication users using any of the "Plain"
security types. Default is \fBvnc\fP.

View File

@ -0,0 +1,24 @@
From 6c8387018b130eb4ef69ea377e9154ba04f0fd50 Mon Sep 17 00:00:00 2001
From: Pierre Ossman <ossman@cendio.se>
Date: Tue, 22 Oct 2024 09:58:27 +0200
Subject: [PATCH] Avoid invalid XFree for XClassHint
It seems XGetClassHint() doesn't set the pointers to NULL if there is no
name, so we need to make sure it is cleared beforehand. Otherwise we can
get an invalid pointer given to XFree().
---
unix/tx/TXWindow.cxx | 1 +
1 file changed, 1 insertion(+)
diff --git a/unix/tx/TXWindow.cxx b/unix/tx/TXWindow.cxx
index b6a29d679..639c13827 100644
--- a/unix/tx/TXWindow.cxx
+++ b/unix/tx/TXWindow.cxx
@@ -313,6 +313,7 @@ void TXWindow::toplevel(const char* name, TXDeleteWindowCallback* dwc_,
void TXWindow::setName(const char* name)
{
XClassHint classHint;
+ memset(&classHint, 0, sizeof(classHint));
XGetClassHint(dpy, win(), &classHint);
XFree(classHint.res_name);
classHint.res_name = (char*)name;

View File

@ -1,29 +0,0 @@
From 6d9017eeb364491cf2acdf1c7e61aee8dd198527 Mon Sep 17 00:00:00 2001
From: Pierre Ossman <ossman@cendio.se>
Date: Fri, 30 Aug 2024 16:15:09 +0200
Subject: [PATCH] Correctly handle ZRLE cursors
Cursor data has a depth of 32 bits and hence cannot use CPIXELs.
This is a regression from baca73d.
---
common/rfb/ZRLEDecoder.cxx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/rfb/ZRLEDecoder.cxx b/common/rfb/ZRLEDecoder.cxx
index 474fd6ca1..e274a697a 100644
--- a/common/rfb/ZRLEDecoder.cxx
+++ b/common/rfb/ZRLEDecoder.cxx
@@ -125,10 +125,10 @@ void ZRLEDecoder::zrleDecode(const Rect& r, rdr::InStream* is,
Pixel maxPixel = pf.pixelFromRGB((uint16_t)-1, (uint16_t)-1, (uint16_t)-1);
bool fitsInLS3Bytes = maxPixel < (1<<24);
bool fitsInMS3Bytes = (maxPixel & 0xff) == 0;
- bool isLowCPixel = (sizeof(T) == 4) &&
+ bool isLowCPixel = (sizeof(T) == 4) && (pf.depth <= 24) &&
((fitsInLS3Bytes && pf.isLittleEndian()) ||
(fitsInMS3Bytes && pf.isBigEndian()));
- bool isHighCPixel = (sizeof(T) == 4) &&
+ bool isHighCPixel = (sizeof(T) == 4) && (pf.depth <= 24) &&
((fitsInLS3Bytes && pf.isBigEndian()) ||
(fitsInMS3Bytes && pf.isLittleEndian()));

View File

@ -0,0 +1,22 @@
From 9e15952d02e01b8e19e7459bcabcd47dc63a1726 Mon Sep 17 00:00:00 2001
From: Pierre Ossman <ossman@cendio.se>
Date: Tue, 22 Oct 2024 09:59:30 +0200
Subject: [PATCH] Do proper top level window setup for selection window
---
unix/x0vncserver/XSelection.cxx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/x0vncserver/XSelection.cxx b/unix/x0vncserver/XSelection.cxx
index 72dd537f4..c724d2ac4 100644
--- a/unix/x0vncserver/XSelection.cxx
+++ b/unix/x0vncserver/XSelection.cxx
@@ -37,7 +37,7 @@ XSelection::XSelection(Display* dpy_, XSelectionHandler* handler_)
probeProperty = XInternAtom(dpy, "TigerVNC_ProbeProperty", False);
transferProperty = XInternAtom(dpy, "TigerVNC_TransferProperty", False);
timestampProperty = XInternAtom(dpy, "TigerVNC_TimestampProperty", False);
- setName("TigerVNC Clipboard (x0vncserver)");
+ toplevel("TigerVNC Clipboard (x0vncserver)");
addEventMask(PropertyChangeMask); // Required for PropertyNotify events
}

View File

@ -1,27 +0,0 @@
From 445e0230cf4e939dcc59caf5d5f001c2f7b04da6 Mon Sep 17 00:00:00 2001
From: Pierre Ossman <ossman@cendio.se>
Date: Thu, 15 Aug 2024 14:24:42 +0200
Subject: [PATCH] Handle existing config directory in vncpasswd
This fixes commit a79c33d.
---
unix/vncpasswd/vncpasswd.cxx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/unix/vncpasswd/vncpasswd.cxx b/unix/vncpasswd/vncpasswd.cxx
index 6666955f1..9f794e129 100644
--- a/unix/vncpasswd/vncpasswd.cxx
+++ b/unix/vncpasswd/vncpasswd.cxx
@@ -213,8 +213,10 @@ int main(int argc, char** argv)
exit(1);
}
if (os::mkdir_p(configDir, 0777) == -1) {
- fprintf(stderr, "Could not create VNC config directory: %s\n", strerror(errno));
- exit(1);
+ if (errno != EEXIST) {
+ fprintf(stderr, "Could not create VNC config directory: %s\n", strerror(errno));
+ exit(1);
+ }
}
snprintf(fname, sizeof(fname), "%s/passwd", configDir);
}

View File

@ -0,0 +1,94 @@
From e26bc65b92d1e43570619deadf20b965e0952fef Mon Sep 17 00:00:00 2001
From: Pat Riehecky <riehecky@fnal.gov>
Date: Wed, 31 Jul 2024 14:43:46 -0500
Subject: [PATCH] vncsession: Move existing log to log.old if present
---
unix/vncserver/vncsession.c | 47 ++++++++++++++++++++++++++++---------
1 file changed, 36 insertions(+), 11 deletions(-)
diff --git a/unix/vncserver/vncsession.c b/unix/vncserver/vncsession.c
index 98a0432aa..a10e0789e 100644
--- a/unix/vncserver/vncsession.c
+++ b/unix/vncserver/vncsession.c
@@ -393,8 +393,9 @@ redir_stdio(const char *homedir, const char *display, char **envp)
int fd;
long hostlen;
char* hostname = NULL, *xdgstate;
- char logfile[PATH_MAX], legacy[PATH_MAX];
+ char logdir[PATH_MAX], logfile[PATH_MAX], logfile_old[PATH_MAX], legacy[PATH_MAX];
struct stat st;
+ size_t fmt_len;
fd = open("/dev/null", O_RDONLY);
if (fd == -1) {
@@ -408,15 +409,24 @@ redir_stdio(const char *homedir, const char *display, char **envp)
close(fd);
xdgstate = getenvp("XDG_STATE_HOME", envp);
- if (xdgstate != NULL && xdgstate[0] == '/')
- snprintf(logfile, sizeof(logfile), "%s/tigervnc", xdgstate);
- else
- snprintf(logfile, sizeof(logfile), "%s/.local/state/tigervnc", homedir);
+ if (xdgstate != NULL && xdgstate[0] == '/') {
+ fmt_len = snprintf(logdir, sizeof(logdir), "%s/tigervnc", xdgstate);
+ if (fmt_len >= sizeof(logdir)) {
+ syslog(LOG_CRIT, "Log dir path too long");
+ _exit(EX_OSERR);
+ }
+ } else {
+ fmt_len = snprintf(logdir, sizeof(logdir), "%s/.local/state/tigervnc", homedir);
+ if (fmt_len >= sizeof(logdir)) {
+ syslog(LOG_CRIT, "Log dir path too long");
+ _exit(EX_OSERR);
+ }
+ }
snprintf(legacy, sizeof(legacy), "%s/.vnc", homedir);
- if (stat(logfile, &st) != 0 && stat(legacy, &st) == 0) {
+ if (stat(logdir, &st) != 0 && stat(legacy, &st) == 0) {
syslog(LOG_WARNING, "~/.vnc is deprecated, please consult 'man vncsession' for paths to migrate to.");
- strcpy(logfile, legacy);
+ strcpy(logdir, legacy);
#ifdef HAVE_SELINUX
/* this is only needed to handle historical type changes for the legacy dir */
@@ -431,9 +441,9 @@ redir_stdio(const char *homedir, const char *display, char **envp)
#endif
}
- if (mkdir_p(logfile, 0755) == -1) {
+ if (mkdir_p(logdir, 0755) == -1) {
if (errno != EEXIST) {
- syslog(LOG_CRIT, "Failure creating \"%s\": %s", logfile, strerror(errno));
+ syslog(LOG_CRIT, "Failure creating \"%s\": %s", logdir, strerror(errno));
_exit(EX_OSERR);
}
}
@@ -450,9 +460,24 @@ redir_stdio(const char *homedir, const char *display, char **envp)
_exit(EX_OSERR);
}
- snprintf(logfile + strlen(logfile), sizeof(logfile) - strlen(logfile), "/%s%s.log",
- hostname, display);
+ fmt_len = snprintf(logfile, sizeof(logfile), "/%s/%s%s.log", logdir, hostname, display);
+ if (fmt_len >= sizeof(logfile)) {
+ syslog(LOG_CRIT, "Log path too long");
+ _exit(EX_OSERR);
+ }
+ fmt_len = snprintf(logfile_old, sizeof(logfile_old), "/%s/%s%s.log.old", logdir, hostname, display);
+ if (fmt_len >= sizeof(logfile)) {
+ syslog(LOG_CRIT, "Log.old path too long");
+ _exit(EX_OSERR);
+ }
free(hostname);
+
+ if (stat(logfile, &st) == 0) {
+ if (rename(logfile, logfile_old) != 0) {
+ syslog(LOG_CRIT, "Failure renaming log file \"%s\" to \"%s\": %s", logfile, logfile_old, strerror(errno));
+ _exit(EX_OSERR);
+ }
+ }
fd = open(logfile, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
syslog(LOG_CRIT, "Failure creating log file \"%s\": %s", logfile, strerror(errno));

View File

@ -1,29 +0,0 @@
From 4db34f73d461b973867ddaf18bf690219229cd7a Mon Sep 17 00:00:00 2001
From: Carlos Santos <casantos@redhat.com>
Date: Thu, 25 Jul 2024 18:39:59 -0300
Subject: [PATCH] vncsession: use /bin/sh if the user shell is not set
An empty shell field in the password file is valid, although not common.
Use /bin/sh in this case, as documented in the passwd(5) man page, since
the vncserver script requires a non-empty SHELL environment variable.
Fixes issue #1786.
Signed-off-by: Carlos Santos <casantos@redhat.com>
---
unix/vncserver/vncsession.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unix/vncserver/vncsession.c b/unix/vncserver/vncsession.c
index 1ee096c7c..98a0432aa 100644
--- a/unix/vncserver/vncsession.c
+++ b/unix/vncserver/vncsession.c
@@ -545,7 +545,7 @@ run_script(const char *username, const char *display, char **envp)
// Set up some basic environment for the script
setenv("HOME", pwent->pw_dir, 1);
- setenv("SHELL", pwent->pw_shell, 1);
+ setenv("SHELL", *pwent->pw_shell != '\0' ? pwent->pw_shell : "/bin/sh", 1);
setenv("LOGNAME", pwent->pw_name, 1);
setenv("USER", pwent->pw_name, 1);
setenv("USERNAME", pwent->pw_name, 1);

View File

@ -4,8 +4,8 @@
%global modulename vncsession
Name: tigervnc
Version: 1.14.0
Release: 2%{?dist}
Version: 1.14.1
Release: 4%{?dist}
Summary: A TigerVNC remote display system
%global _hardened_build 1
@ -23,14 +23,16 @@ Source5: vncserver
# Downstream patches
Patch1: tigervnc-use-gnome-as-default-session.patch
# https://github.com/TigerVNC/tigervnc/pull/1425
Patch2: tigervnc-vncsession-restore-script-systemd-service.patch
# https://github.com/TigerVNC/tigervnc/pull/1792
Patch3: tigervnc-add-option-allowing-to-connect-only-user-owning-session.patch
# Upstream patches
Patch50: tigervnc-vncsession-use-bin-sh-when-shell-not-set.patch
Patch51: tigervnc-add-missing-coma-in-default-security-type-list.patch
Patch52: tigervnc-vncsession-move-existing-log-to-log-old-if-present.patch
Patch53: tigervnc-handle-existing-config-directory-in-vncpasswd.patch
Patch54: tigervnc-correctly-handle-zrle-cursors.patch
Patch50: tigervnc-vncsession-move-existing-log-to-log-old-if-present.patch
Patch51: tigervnc-add-clipboard-support-to-x0vncserver.patch
Patch52: tigervnc-do-proper-toplevel-window-setup-for-selection-window.patch
Patch53: tigervnc-avoid-invalid-xfree-for-xclasshint.patch
# Upstreamable patches
Patch80: tigervnc-dont-get-pointer-position-for-floating-device.patch
@ -91,7 +93,13 @@ BuildRequires: xorg-x11-util-macros
BuildRequires: xorg-x11-xtrans-devel
# SELinux
BuildRequires: libselinux-devel, selinux-policy-devel, systemd
BuildRequires: libselinux-devel
BuildRequires: selinux-policy-devel
# For RHEL-34880
BuildRequires: pkgconfig(dbus-1) >= 1.0
BuildRequires: pkgconfig(libsystemd) >= 209
BuildRequires: pkgconfig(libudev) >= 143
Requires(post): coreutils
Requires(postun):coreutils
@ -199,13 +207,13 @@ popd
# Tigervnc patches
%patch -P1 -p1 -b .use-gnome-as-default-session
%patch -P2 -p1 -b .vncsession-restore-script-systemd-service
%patch -P3 -p1 -b .add-option-allowing-to-connect-only-user-owning-session
# Upstream patches
%patch -P50 -p1 -b .vncsession-use-bin-sh-when-shell-not-set
%patch -P51 -p1 -b .add-missing-coma-in-default-security-type-list
%patch -P52 -p1 -b .vncsession-move-existing-log-to-log-old-if-present
%patch -P53 -p1 -b .handle-existing-config-directory-in-vncpasswd
%patch -P54 -p1 -b .correctly-handle-zrle-cursors.patch
%patch -P50 -p1 -b .vncsession-move-existing-log-to-log-old-if-present
%patch -P51 -p1 -b .add-clipboard-support-to-x0vncserver
%patch -P52 -p1 -b .do-proper-toplevel-window-setup-for-selection-window
%patch -P53 -p1 -b .avoid-invalid-xfree-for-xclasshint
# Upstreamable patches
%patch -P80 -p1 -b .dont-get-pointer-position-for-floating-device
@ -247,7 +255,9 @@ autoreconf -fiv
--disable-config-udev \
--without-dtrace \
--disable-devel-docs \
--disable-selective-werror
--disable-selective-werror \
--enable-systemd-logind \
--enable-config-udev
make %{?_smp_mflags}
popd
@ -390,7 +400,40 @@ fi
%ghost %verify(not md5 size mode mtime) %{_sharedstatedir}/selinux/%{selinuxtype}/active/modules/200/%{modulename}
%changelog
* Tue Jul 23 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.0-2
* Tue Jan 21 2025 Jan Grulich <jgrulich@redhat.com> - 1.14.1-4
- Fix crash in clipboard support in x0vncserver
Resolves: RHEL-74216
* Thu Jan 16 2025 Jan Grulich <jgrulich@redhat.com> - 1.14.1-3
- Add clipboard support to x0vncserver
Resolves: RHEL-74216
* Thu Oct 31 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.1-2
- Fix CVE-2024-9632: xorg-x11-server: heap-based buffer overflow privilege escalation vulnerability
Resolves: RHEL-62001
* Wed Oct 23 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.1-1
- 1.14.1
Resolves: RHEL-45316
* Mon Oct 07 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.0-6
- Make "ApproveLoggedUserOnly" to ignore "closing" sessions
Resolves: RHEL-34880
* Fri Oct 04 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.0-5
- Fix "ApproveLoggedUserOnly" option not working in some setups
Resolves: RHEL-34880
* Fri Sep 27 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.0-4
- Add option "ApproveLoggedUserOnly" allowing to connect only the user
owning the running session
Resolves: RHEL-34880
* Wed Sep 04 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.0-3
- Move old log to log.old if present (fix patch)
Resolves: RHEL-54294
* Tue Aug 20 2024 Jan Grulich <jgrulich@redhat.com> - 1.14.0-2
- 1.14.0
Resolves: RHEL-45316
- Move old log to log.old if present