Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option allowing to connect only the user owning the running session #1792

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions common/rfb/VNCServerST.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -680,13 +680,6 @@ void VNCServerST::queryConnection(VNCSConnectionST* client,
return;
}

// - Are we configured to do queries?
if (!rfb::Server::queryConnect &&
!client->getSock()->requiresQuery()) {
approveConnection(client->getSock(), true, nullptr);
return;
}

// - Does the client have the right to bypass the query?
if (client->accessCheck(AccessNoQuery))
{
Expand Down
138 changes: 137 additions & 1 deletion unix/xserver/hw/vnc/XserverDesktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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_,
Expand Down Expand Up @@ -165,11 +178,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;
Expand Down
7 changes: 7 additions & 0 deletions unix/xserver/hw/vnc/XserverDesktop.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
void grabRegion(const rfb::Region& r) override;

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);
Expand Down
7 changes: 7 additions & 0 deletions unix/xserver/hw/vnc/Xvnc.man
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading