diff options
author | Sebastien Braun | 2010-10-05 15:15:36 +0200 |
---|---|---|
committer | Sebastien Braun | 2010-10-05 18:15:50 +0200 |
commit | ee159229bcfb6f2dbd19ef16e37fa2c65cd3846d (patch) | |
tree | 2a2abe7105f23acc4dee555eb3a6e2ca77fe1ff8 /src/input | |
parent | Refactor InputEvent handler code. (diff) | |
download | pvs-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.cpp | 36 | ||||
-rw-r--r-- | src/input/inputEventHandler.h | 4 | ||||
-rw-r--r-- | src/input/pvsCheckPrivileges.cpp | 314 | ||||
-rw-r--r-- | src/input/pvsCheckPrivileges.h | 139 |
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_ */ |