summaryrefslogtreecommitdiffstats
path: root/src/input
diff options
context:
space:
mode:
authorSebastien Braun2010-10-05 15:15:36 +0200
committerSebastien Braun2010-10-05 18:15:50 +0200
commitee159229bcfb6f2dbd19ef16e37fa2c65cd3846d (patch)
tree2a2abe7105f23acc4dee555eb3a6e2ca77fe1ff8 /src/input
parentRefactor InputEvent handler code. (diff)
downloadpvs-ee159229bcfb6f2dbd19ef16e37fa2c65cd3846d.tar.gz
pvs-ee159229bcfb6f2dbd19ef16e37fa2c65cd3846d.tar.xz
pvs-ee159229bcfb6f2dbd19ef16e37fa2c65cd3846d.zip
Add Permission checking and session information code.
Diffstat (limited to 'src/input')
-rw-r--r--src/input/inputEventHandler.cpp36
-rw-r--r--src/input/inputEventHandler.h4
-rw-r--r--src/input/pvsCheckPrivileges.cpp314
-rw-r--r--src/input/pvsCheckPrivileges.h139
4 files changed, 493 insertions, 0 deletions
diff --git a/src/input/inputEventHandler.cpp b/src/input/inputEventHandler.cpp
new file mode 100644
index 0000000..c16c358
--- /dev/null
+++ b/src/input/inputEventHandler.cpp
@@ -0,0 +1,36 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include "inputEventHandler.h"
+#include "pvsCheckPrivileges.h"
+
+bool policy::allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx)
+{
+ if(ctx)
+ return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_UNKNOWN, PVSCheckPrivileges::USER_PRIVILEGED,
+ ctx);
+ else
+ return false;
+}
+
+bool policy::allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx)
+{
+ if(ctx)
+ return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_LOCAL, PVSCheckPrivileges::USER_UNKNOWN,
+ ctx);
+ else
+ return false;
+}
diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h
index 330f5a7..a0ada2e 100644
--- a/src/input/inputEventHandler.h
+++ b/src/input/inputEventHandler.h
@@ -100,6 +100,10 @@ struct Security
{
bool allow(InputEvent const& evt, InputEventContext const* ctx)
{
+ if((flags & SEC_PHYSICAL_SEAT) && !allowPhysicalSeat(evt, ctx))
+ return false;
+ if((flags & SEC_PRIVILEGED_USER) && !allowPrivilegedUser(evt, ctx))
+ return false;
return true;
}
};
diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp
new file mode 100644
index 0000000..56073bc
--- /dev/null
+++ b/src/input/pvsCheckPrivileges.cpp
@@ -0,0 +1,314 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsCheckPrivileges.cpp:
+ # - A small program that checks whether or not it is called from
+ # a physical seat and conditionally executes the pvs input daemon.
+ # Additional security-relevant conditions should be checked here.
+ #
+ # The program is called with exactly one parameter, specifying the
+ # number of the file descriptor which is to be passed to its child.
+ # --------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include <iostream>
+#include <cerrno>
+#include <cstdlib>
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+#include <QTimer>
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusMetaType>
+#include <QtDBus/QDBusReply>
+#include <QtGlobal>
+#include <QDebug>
+#include <QUuid>
+#include "pvsCheckPrivileges.h"
+
+using namespace std;
+
+#define TIMEOUT_VALUE 10000 /* Wait for max. 10 seconds */
+
+// We need these classes for PolicyKit access:
+struct PolKitSubject {
+ QString subject_kind;
+ QMap<QString, QVariant> subject_details;
+};
+Q_DECLARE_METATYPE(PolKitSubject);
+
+QDBusArgument& operator<<(QDBusArgument& arg, PolKitSubject const& subj)
+{
+ arg.beginStructure();
+ arg << subj.subject_kind << subj.subject_details;
+ arg.endStructure();
+ return arg;
+}
+
+QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitSubject& subj)
+{
+ arg.beginStructure();
+ arg >> subj.subject_kind >> subj.subject_details;
+ arg.endStructure();
+ return arg;
+}
+
+struct PolKitAuthReply {
+ bool isAuthorized;
+ bool isChallenge;
+ QMap<QString, QString> details;
+};
+Q_DECLARE_METATYPE(PolKitAuthReply);
+
+QDBusArgument& operator<<(QDBusArgument& arg, PolKitAuthReply const& reply)
+{
+ arg.beginStructure();
+ arg << reply.isAuthorized << reply.isChallenge << reply.details;
+ arg.endStructure();
+ return arg;
+}
+
+QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitAuthReply& reply)
+{
+ arg.beginStructure();
+ arg >> reply.isAuthorized >> reply.isChallenge >> reply.details;
+ arg.endStructure();
+ return arg;
+}
+
+// We need to pass QMap<QString, QString> to QVariant:
+typedef QMap<QString, QString> QStringStringMap;
+Q_DECLARE_METATYPE(QStringStringMap)
+
+PVSCheckPrivileges* PVSCheckPrivileges::instance()
+{
+ static PVSCheckPrivileges static_instance;
+ return &static_instance;
+}
+
+PVSCheckPrivileges::PVSCheckPrivileges()
+{
+}
+
+PVSCheckPrivileges::~PVSCheckPrivileges()
+{
+}
+
+QString PVSCheckPrivileges::getSessionReference(CachedInputContext const& sender)
+{
+ if(!sender.isValid())
+ {
+ return QString();
+ }
+
+ QString sessionReference = _savedConsoleKitSession.value(sender, QString());
+ if(sessionReference.isNull())
+ {
+ QDBusConnection conn = QDBusConnection::systemBus();
+ // Find the name of the current session:
+ QDBusInterface manager("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", conn);
+ QDBusReply<QDBusObjectPath> replyGetSession = manager.call(QDBus::Block, "GetSessionForUnixProcess", (quint32)sender.pid);
+ if(!replyGetSession.isValid())
+ {
+ qWarning("Reply to GetSessionForUnixProcess is invalid: %s: %s", replyGetSession.error().name().toLocal8Bit().constData(), replyGetSession.error().message().toLocal8Bit().constData());
+ return QString();
+ }
+ _savedConsoleKitSession[sender] = sessionReference = replyGetSession.value().path();
+ }
+ return sessionReference;
+}
+
+PVSCheckPrivileges::SessionKind PVSCheckPrivileges::getSessionKind(CachedInputContext const& sender)
+{
+ if(!sender.isValid())
+ {
+ return SESSION_UNKNOWN;
+ }
+
+ // if the sender is root, we always suppose he works locally.
+ if(sender.uid == 0)
+ {
+ return SESSION_LOCAL;
+ }
+
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return SESSION_LOOKUP_FAILURE;
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface session("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<bool> replyIsLocal = session.call(QDBus::Block, "IsLocal");
+
+ if(!replyIsLocal.isValid())
+ {
+ qWarning("Unable to find out whether the current session is local: %s: %s", replyIsLocal.error().name().toLocal8Bit().constData(), replyIsLocal.error().message().toLocal8Bit().constData());
+ return SESSION_LOOKUP_FAILURE;
+ }
+ return replyIsLocal.value() ? SESSION_LOCAL : SESSION_NONLOCAL;
+}
+
+PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInputContext const& sender)
+{
+ // Always allow root:
+ if(sender.uid == 0)
+ return USER_PRIVILEGED;
+
+ // For PolKit, we need the start-time of the process.
+ // On Linux, we can get it from /proc:
+ QString procStat = QString("/proc/%1/stat").arg(sender.pid);
+ QFile procStatFile(procStat);
+ if(!procStatFile.exists())
+ {
+ qWarning("Could not look up any info on process %d, its %s file does not exist", sender.pid, procStat.toLocal8Bit().constData());
+ return USER_LOOKUP_FAILURE;
+ }
+ procStatFile.open(QIODevice::ReadOnly);
+ QByteArray procStatBytes = procStatFile.readAll();
+ qDebug() << "Read stat file: " << procStatBytes;
+ QString procStatLine = QString::fromLocal8Bit(procStatBytes.constData(), procStatBytes.length());
+ QStringList procStatFields = procStatLine.split(QRegExp("\\s+"));
+ qDebug() << "Found stat fields: " << procStatFields;
+ bool ok;
+ quint64 startTime = procStatFields[21].toULongLong(&ok);
+ if(!ok)
+ {
+ qWarning("Could not find out start time for process %d", (int)sender.pid);
+ return USER_LOOKUP_FAILURE;
+ }
+
+ // Okay, we got the startTime. Now ask PolicyKit:
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", conn);
+ PolKitSubject subj;
+ subj.subject_kind = "unix-process";
+ subj.subject_details["pid"] = (quint32)sender.pid;
+ subj.subject_details["start-time"] = startTime;
+ QDBusReply<PolKitAuthReply> reply = intf.call(QDBus::Block,
+ QLatin1String("CheckAuthorization"),
+ QVariant::fromValue(subj),
+ "org.openslx.pvs.privilegedinput",
+ QVariant::fromValue(QMap<QString, QString>()) /* No details */,
+ (quint32)1 /* Allow Interaction */,
+ QUuid::createUuid().toString() /* Cancellation ID */);
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to CheckAuthorization is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return USER_LOOKUP_FAILURE;
+ }
+ return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED;
+}
+
+QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender)
+{
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return QString();
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<QString> reply = intf.call(QDBus::Block, QLatin1String("GetX11Display"));
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to GetX11Display is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return QString();
+ }
+
+ return reply.value();
+}
+
+QString PVSCheckPrivileges::getX11DisplayDevice(CachedInputContext const& sender)
+{
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return QString();
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<QString> reply = intf.call(QDBus::Block, QLatin1String("GetX11DisplayDevice"));
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to GetX11DisplayDevice is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return QString();
+ }
+
+ return reply.value();
+}
+
+bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext const& sender)
+{
+ SessionKind cachedSessionKind;
+
+ if(sessionKind < SESSION_NONLOCAL)
+ {
+ if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN)
+ {
+ cachedSessionKind = getSessionKind(sender);
+ if(cachedSessionKind != SESSION_LOOKUP_FAILURE)
+ _savedSessionKind[sender] = cachedSessionKind;
+ qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData());
+ }
+ if(cachedSessionKind > sessionKind)
+ return false;
+ }
+
+ return true;
+}
+
+bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender)
+{
+ UserPrivilege cachedUserPrivilege;
+
+ if(userPrivilege < USER_UNPRIVILEGED)
+ {
+ if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN)
+ {
+ cachedUserPrivilege = getUserPrivilege(sender);
+ if(cachedUserPrivilege != USER_LOOKUP_FAILURE)
+ _savedUserPrivilege[sender] = cachedUserPrivilege;
+ qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData());
+ }
+ if(cachedUserPrivilege > userPrivilege)
+ return false;
+ }
+ return true;
+}
+
+bool PVSCheckPrivileges::require(SessionKind sessionKind,
+ UserPrivilege userPrivilege,
+ CachedInputContext const& sender)
+{
+ if(!require(sessionKind, sender))
+ return false;
+ if(!require(userPrivilege, sender))
+ return false;
+ return true;
+}
+
+uint qHash(CachedInputContext const& p)
+{
+ return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid)));
+}
diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h
new file mode 100644
index 0000000..e5cc9a4
--- /dev/null
+++ b/src/input/pvsCheckPrivileges.h
@@ -0,0 +1,139 @@
+/*
+ # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # src/net/pvsCheckPrivileges_linux.h
+ # - Linux implementation of privilege checking
+ # -----------------------------------------------------------------------------
+ */
+
+#ifndef PVSCHECKPRIVILEGES_H_
+#define PVSCHECKPRIVILEGES_H_
+
+#include <sys/types.h>
+#include <QString>
+#include <QHash>
+#include "inputEventHandler.h"
+
+struct CachedInputContext
+{
+ CachedInputContext(InputEventContext const* source)
+ {
+ if(source)
+ {
+ pid = source->getSenderPid();
+ uid = source->getSenderUid();
+ gid = source->getSenderGid();
+ }
+ else
+ {
+ pid = (pid_t)-1;
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ }
+ }
+
+ CachedInputContext()
+ {
+ pid = (pid_t)-1;
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ }
+
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+
+ bool isValid() const
+ {
+ return (pid != (pid_t)-1) && (uid != (uid_t)-1) && (gid != (gid_t)-1);
+ }
+
+ bool operator==(CachedInputContext const& other) const
+ {
+ return (other.pid == pid) && (other.uid == uid) && (other.gid == gid);
+ }
+};
+uint qHash(CachedInputContext const& p);
+
+class PVSCheckPrivileges
+{
+public:
+ typedef enum {
+ SESSION_LOOKUP_FAILURE, // Comes first because we default to assume
+ // the session is local if we cannot look it
+ // up.
+ SESSION_LOCAL,
+ SESSION_NONLOCAL,
+ SESSION_UNKNOWN
+ } SessionKind;
+ static QString toString(SessionKind k)
+ {
+ switch(k)
+ {
+ case SESSION_LOOKUP_FAILURE: return "SESSION_LOOKUP_FAILURE";
+ case SESSION_LOCAL: return "SESSION_LOCAL";
+ case SESSION_NONLOCAL: return "SESSION_NONLOCAL";
+ case SESSION_UNKNOWN: return "SESSION_UNKNOWN";
+ default: return QString("unknown value (%1)").arg(k);
+ }
+ }
+
+ typedef enum {
+ USER_PRIVILEGED,
+ USER_UNPRIVILEGED,
+ USER_LOOKUP_FAILURE, // Comes last because we default to assume
+ // the user is unprivileged if we cannot get
+ // permission from PolicyKit.
+ USER_UNKNOWN
+ } UserPrivilege;
+ static QString toString(UserPrivilege k)
+ {
+ switch(k)
+ {
+ case USER_PRIVILEGED: return "USER_PRIVILEGED";
+ case USER_UNPRIVILEGED: return "USER_UNPRIVILEGED";
+ case USER_LOOKUP_FAILURE: return "USER_LOOKUP_FAILURE";
+ case USER_UNKNOWN: return "USER_UNKNOWN";
+ default: return QString("unknown value (%1)").arg(k);
+ }
+ }
+
+ static PVSCheckPrivileges* instance();
+
+ bool require(SessionKind sessionKind, CachedInputContext const& sender);
+ bool require(UserPrivilege userPrivilege, CachedInputContext const& sender);
+ bool require(SessionKind sessionKind, UserPrivilege userPrivilege, CachedInputContext const& sender);
+ QString getX11SessionName(CachedInputContext const& sender);
+ QString getX11DisplayDevice(CachedInputContext const& sender);
+
+private:
+ PVSCheckPrivileges();
+ virtual ~PVSCheckPrivileges();
+
+ typedef QPair<pid_t, QPair<uid_t, gid_t> > piduidgid;
+ piduidgid makePidUidGid(pid_t pid, uid_t uid, gid_t gid)
+ {
+ return qMakePair(pid, qMakePair(uid, gid));
+ }
+
+ QString getSessionReference(CachedInputContext const& sender);
+ SessionKind getSessionKind(CachedInputContext const& sender);
+ UserPrivilege getUserPrivilege(CachedInputContext const& sender);
+
+ static PVSCheckPrivileges* _instance;
+
+ QHash<CachedInputContext, UserPrivilege> _savedUserPrivilege;
+ QHash<CachedInputContext, SessionKind> _savedSessionKind;
+ QHash<CachedInputContext, QString> _savedConsoleKitSession;
+};
+
+#endif /* PVSCHECKPRIVILEGES_H_ */