From 266eb5fb14c07e67aa211a5860e9abf3009136e3 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Mon, 4 Oct 2010 00:22:14 +0200 Subject: Implement first version of basic input event support --- src/input/CMakeLists.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/input/CMakeLists.txt (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt new file mode 100644 index 0000000..aa52ec3 --- /dev/null +++ b/src/input/CMakeLists.txt @@ -0,0 +1,19 @@ +INCLUDE(${QT_USE_FILE}) + +SET(pvsinput_SRCS + inputEvent.cpp + ) + +IF(UNIX) + list(APPEND pvsinput_SRCS + x11InputUtils.cpp + x11FakeKeyboardHandler.cpp + x11FakeMouseHandler.cpp) +ENDIF() + +ADD_LIBRARY( + pvsinput + STATIC + ${pvsinput_HDRS} + ${pvsinput_SRCS} +) -- cgit v1.2.3-55-g7522 From ac05f7d367ae9983e7996738871efbdbf3ec66e8 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:30:04 +0200 Subject: Implement privileged input daemon, first version without handlers. --- src/input/CMakeLists.txt | 45 ++++++- src/input/inputHandlerChain.h | 8 +- src/input/privilegedHandlerForwarder.cpp | 82 +++++++++++++ src/input/privilegedHandlerForwarder.h | 32 +++++ src/input/pvsPrivInputHandler.cpp | 108 +++++++++++++++++ src/input/pvsPrivInputHandler.h | 45 +++++++ src/input/pvsPrivInputSocket.cpp | 196 +++++++++++++++++++++++++++++++ src/input/pvsPrivInputSocket.h | 31 +++++ src/input/pvsprivinputd.cpp | 147 +++++++++++++++++++++++ 9 files changed, 687 insertions(+), 7 deletions(-) create mode 100644 src/input/privilegedHandlerForwarder.cpp create mode 100644 src/input/privilegedHandlerForwarder.h create mode 100644 src/input/pvsPrivInputHandler.cpp create mode 100644 src/input/pvsPrivInputHandler.h create mode 100644 src/input/pvsPrivInputSocket.cpp create mode 100644 src/input/pvsPrivInputSocket.h create mode 100644 src/input/pvsprivinputd.cpp (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index aa52ec3..5f009c5 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -2,18 +2,51 @@ INCLUDE(${QT_USE_FILE}) SET(pvsinput_SRCS inputEvent.cpp + inputEventHandler.cpp ) + +if(UNIX) + set(pvsprivinputd_SRCS + pvsprivinputd.cpp + pvsPrivInputHandler.cpp + pvsCheckPrivileges.cpp + ) + + set(pvsprivinputd_MOC_HDRS + pvsPrivInputHandler.h + ) -IF(UNIX) - list(APPEND pvsinput_SRCS - x11InputUtils.cpp + qt4_wrap_cpp(pvsprivinputd_MOC_SRCS + ${pvsprivinputd_MOC_HDRS} + ) + + add_executable(pvsprivinputd + ${pvsprivinputd_SRCS} + ${pvsprivinputd_MOC_SRCS} + ) + + set_property(SOURCE ${pvsprivinputd_SRCS} ${pvsprivinputd_MOC_SRCS} + APPEND + PROPERTY COMPILE_FLAGS " -I${QT_QTCORE_INCLUDE_DIR} -I${QT_QTDBUS_INCLUDE_DIR} -I${QT_QTNETWORK_INCLUDE_DIR}") + + target_link_libraries(pvsprivinputd + pvsinput + ${QT_QTCORE_LIBRARY} + ${QT_QTDBUS_LIBRARY} + ${QT_QTNETWORK_LIBRARY} + ) + + list(APPEND pvsinput_SRCS + pvsPrivInputSocket.cpp + x11InputUtils.cpp x11FakeKeyboardHandler.cpp - x11FakeMouseHandler.cpp) -ENDIF() + x11FakeMouseHandler.cpp + privilegedHandlerForwarder.cpp + ) +endif() ADD_LIBRARY( pvsinput STATIC - ${pvsinput_HDRS} ${pvsinput_SRCS} ) diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 4bb9fe5..61c3d62 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -22,13 +22,19 @@ #include "x11FakeKeyboardHandler.h" #include "x11FakeMouseHandler.h" +#include "privilegedHandlerForwarder.h" typedef boost::mpl::list< Handler >, Handler >, - Handler > + Handler >, + Handler >::type unprivileged_handler_list; typedef InputEventHandlerChain unprivileged_handler_chain; +typedef boost::mpl::list< +>::type privileged_handler_list; + +typedef InputEventHandlerChain privileged_handler_chain; #endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/privilegedHandlerForwarder.cpp b/src/input/privilegedHandlerForwarder.cpp new file mode 100644 index 0000000..3c0b3bd --- /dev/null +++ b/src/input/privilegedHandlerForwarder.cpp @@ -0,0 +1,82 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # inputHandlerChain.cpp: + # - Forward privileged input events to a special handler process - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" +#include "privilegedHandlerForwarder.h" + +using namespace std; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 /* according to unix(7) */ +#endif + +void PrivilegedHandlerForwarder::initialize() +{ + QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsinputd"); + + QString defaultPath = "/tmp/pvsprivinputd.sock"; + QByteArray socketPath = settings.value("socketpath", defaultPath).toString().toLocal8Bit(); + + _socket = pvsPrivInputMakeClientSocket(); + if(_socket < 0) + { + return; + } +} + +void PrivilegedHandlerForwarder::handle(InputEvent const& evt, InputEventContext const*) +{ + qDebug("Trying to handle %s in PrivilegedHandlerForwarder", evt.toString().c_str()); + if(_socket < 0) + { + initialize(); + } + + QByteArray data; + QDataStream strm(&data, QIODevice::WriteOnly); + strm.setByteOrder(QDataStream::BigEndian); + strm << evt; + + assert(data.size() == 8); + + int delta = 0; + int count = 0; + do { + delta = write(_socket, data.constData(), data.size()); + if(delta < 0) + { + qWarning("Error while communicating with pvsprivinputd: %s", strerror(errno)); + close(_socket); + + // Try again: + initialize(); + } + else if(delta != 8) + { + // This should normally not happen. + qWarning("Could not send a complete packet. Only %d bytes sent", delta); + } + count++; + } while(delta != 8 && count < 3); +} diff --git a/src/input/privilegedHandlerForwarder.h b/src/input/privilegedHandlerForwarder.h new file mode 100644 index 0000000..37e8f24 --- /dev/null +++ b/src/input/privilegedHandlerForwarder.h @@ -0,0 +1,32 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # inputHandlerChain.h: + # - Forward privileged input events to a special handler process. + # -------------------------------------------------------------------------- + */ + +#ifndef PRIVILEGEDHANDLERFORWARDER_H_ +#define PRIVILEGEDHANDLERFORWARDER_H_ + +#include "inputEventHandler.h" + +class PrivilegedHandlerForwarder : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const& evt, InputEventContext const* = 0); + void initialize(); + +private: + int _socket; +}; + +#endif /* PRIVILEGEDHANDLERFORWARDER_H_ */ diff --git a/src/input/pvsPrivInputHandler.cpp b/src/input/pvsPrivInputHandler.cpp new file mode 100644 index 0000000..70ed1bc --- /dev/null +++ b/src/input/pvsPrivInputHandler.cpp @@ -0,0 +1,108 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsPrivInputHandler.h + # - Handle privileged input connection requests - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "inputEvent.h" +#include "inputEventHandler.h" +#include "pvsPrivInputSocket.h" +#include "pvsPrivInputHandler.h" + +using namespace std; + +class PrivInputContext : public InputEventContext +{ +public: + PrivInputContext(pid_t pid, uid_t uid, gid_t gid) + : _pid(pid), _uid(uid), _gid(gid) + { + } + + pid_t getSenderPid() const + { + return _pid; + } + + uid_t getSenderUid() const + { + return _uid; + } + + gid_t getSenderGid() const + { + return _gid; + } + +private: + pid_t _pid; + uid_t _uid; + gid_t _gid; +}; + +PVSPrivInputHandler::PVSPrivInputHandler(int fd, QObject* parent) : + QObject(parent) +{ + _fd = fd; + _notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + _notifier->setEnabled(true); + connect(_notifier, SIGNAL(activated(int)), this, SLOT(canRead())); +} + +PVSPrivInputHandler::~PVSPrivInputHandler() +{ + delete _notifier; +} + +void PVSPrivInputHandler::canRead() +{ + _notifier->setEnabled(false); + + // We need to retrieve the credentials of the process: + size_t siz = 8; + _messageAssembly.clear(); + _messageAssembly.resize(8); + pid_t pid; + uid_t uid; + gid_t gid; + int err; + + if(!pvsPrivInputRecvMessage(_fd, _messageAssembly.data(), siz, pid, uid, gid, &err)) + { + close(_fd); + deleteLater(); + return; + } + else + { + if(siz != 8) + { + qWarning("Malformed PVS Input Event packet"); + return; + } + QDataStream strm(&_messageAssembly, QIODevice::ReadOnly); + InputEvent evt; + strm.setByteOrder(QDataStream::BigEndian); + strm >> evt; + PrivInputContext ctx(pid, uid, gid); + _handlerChain.handle(evt, &ctx); + } + + _notifier->setEnabled(true); +} diff --git a/src/input/pvsPrivInputHandler.h b/src/input/pvsPrivInputHandler.h new file mode 100644 index 0000000..9980cdf --- /dev/null +++ b/src/input/pvsPrivInputHandler.h @@ -0,0 +1,45 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsPrivInputHandler.h + # - Handle privileged input connection requests - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSPRIVINPUTHANDLER_H_ +#define PVSPRIVINPUTHANDLER_H_ + +#include +#include +#include +#include "inputHandlerChain.h" + +class QSocketNotifier; + +class PVSPrivInputHandler : public QObject +{ + Q_OBJECT +public: + PVSPrivInputHandler(int fd, QObject* parent = 0); + virtual ~PVSPrivInputHandler(); + +private slots: + void canRead(); + +private: + QSocketNotifier* _notifier; + QByteArray _messageAssembly; + int _bytes; + int _fd; + privileged_handler_chain _handlerChain; +}; + +#endif /* PVSPRIVINPUTHANDLER_H_ */ diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp new file mode 100644 index 0000000..2428582 --- /dev/null +++ b/src/input/pvsPrivInputSocket.cpp @@ -0,0 +1,196 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsPrivInputSocket.h: + # - Centralize knowledge of socket address and connection options + # for pvsprivinputd - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" + +using namespace std; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 /* according to unix(7) */ +#endif + +QString pvsPrivInputGetSocketAddress() +{ + QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsprivinputd"); + return settings.value("socketpath", "/tmp/pvsprivinputd.sock").toString(); +} + +int pvsPrivInputMakeClientSocket() +{ + int sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if(sock < 0) + { + qWarning("Could not create a socket: %s", strerror(errno)); + return -1; + } + + QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1); + if(connect(sock, reinterpret_cast(&addr), + sizeof(addr)) < 0) + { + qWarning("Could not connect to pvsprivinputd at %s: %s", socketPath.constData(), strerror(errno)); + close(sock); + return -1; + } + + return sock; +} + +int pvsPrivInputMakeServerSocket() +{ + int sock = socket(AF_UNIX, SOCK_DGRAM, 0); + if(sock < 0) + { + qCritical("Could not create a socket: %s", strerror(errno)); + return -1; + } + + // Bind to the address: + QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1); + if(bind(sock, reinterpret_cast(&addr), sizeof(addr)) < 0) + { + qCritical("Could not bind socket to %s", strerror(errno)); + close(sock); + return -1; + } + + // Announce that credentials are requested: + int passcred = 1; + if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0) + { + // We will not operate without credentials. + qCritical("Could not request peer credentials: %s", strerror(errno)); + close(sock); + return -1; + } + +#if 0 /* Only for SOCK_STREAM: */ + // Listen for connections: + if(listen(sock, 1) < 0) + { + qCritical("Could not listen for connections to %s: %s", socketPath.constData(), strerror(errno)); + close(sock); + return -1; + } +#endif + + return sock; +} + +bool pvsPrivInputSendMessage(int sock, void* buf, size_t _len, int* err) +{ + /* + * Portability note: All UNIX-like systems can transmit credentials over UNIX + * sockets, but only Linux does it automagically. + */ + + long len = (long)_len; + + // send(2) does not split messages on a SOCK_DGRAM socket. + int e = send(sock, buf, len, 0); + if(e < 0) + { + qWarning("Failed to send message of length %d over socket %d: %s", (unsigned)len, e, strerror(errno)); + if(err) + *err = errno; + return false; + } + else if(e < len) + { + qWarning("Failed to send a complete message of length %d over socket %d, only %d bytes were sent", (unsigned)len, sock, e); + if(err) + *err = errno; + return false; + } + + return true; +} + +bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len, + pid_t& pid, uid_t& uid, gid_t& gid, int* err) +{ + struct iovec iov; + struct msghdr msg; + char ctlbuf[1024]; + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctlbuf; + msg.msg_controllen = sizeof(ctlbuf); + msg.msg_flags = 0; + int bytes_read = recvmsg(sock, &msg, 0); + if(bytes_read < 0) + { + qWarning("Could not read from socket: %s", strerror(errno)); + if(err) + *err = errno; + return false; + } + + pid = -1; + uid = -1; + gid = -1; + + struct cmsghdr* cmsg; + for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) + { + struct ucred* creds = reinterpret_cast(CMSG_DATA(cmsg)); + pid = creds->pid; + uid = creds->uid; + gid = creds->gid; + break; + } + else if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + // We need to close passed file descriptors. If we don't, we + // have a denial-of-service vulnerability. + int* fds = reinterpret_cast(CMSG_DATA(cmsg)); + unsigned num_fds = cmsg->cmsg_len / sizeof(int); + for(unsigned i = 0; i < num_fds; i++) + { + close(fds[i]); + } + } + } + + if(pid == (pid_t)-1 || uid == (uid_t)-1 || gid == (gid_t)-1) + { + *err = 0; + return false; + } + + return true; +} diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h new file mode 100644 index 0000000..e6fb0c0 --- /dev/null +++ b/src/input/pvsPrivInputSocket.h @@ -0,0 +1,31 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsPrivInputSocket.h: + # - Centralize knowledge of socket address and connection options + # for pvsprivinputd - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSPRIVINPUTSOCKET_H_ +#define PVSPRIVINPUTSOCKET_H_ + +#include +#include +#include + +QString pvsPrivInputGetSocketAddress(); +int pvsPrivInputMakeClientSocket(); +int pvsPrivInputMakeServerSocket(); +bool pvsPrivInputSendMessage(int sock, void* buf, size_t len, int* err = 0); +bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len, pid_t& pid, uid_t& uid, gid_t& gid, int* err = 0); + +#endif /* PVSPRIVINPUTSOCKET_H_ */ diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp new file mode 100644 index 0000000..3a5e1a8 --- /dev/null +++ b/src/input/pvsprivinputd.cpp @@ -0,0 +1,147 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsprivinputd.cpp: + # - Handle privileged input events - daemon + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" +#include "pvsPrivInputHandler.h" + +using namespace std; + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 /* according to unix(7) */ +#endif + +QByteArray socketPath; +int sigintFds[2]; + +static void unlinkSocket() +{ + if(!socketPath.isNull()) + { + unlink(socketPath.constData()); + } +} + +static void onInterrupt(int) +{ + char buf[] = { '!' }; + char msg[] = "SIGINT caught. Quitting.\n"; + write(sigintFds[0], buf, 1); + write(1, msg, sizeof(msg)-1); +} + +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + app.setApplicationName("pvsprivinputd"); + app.setOrganizationName("openslx"); + + bool no_fork = app.arguments().contains("--no-fork", Qt::CaseInsensitive); + if(!no_fork) + { + pid_t pid; + pid_t sid; + + pid = fork(); + if(pid < 0) + { + qCritical("Could not fork: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + if(pid > 0) + { + exit(EXIT_SUCCESS); + } + + // We are now inside a child process. + // Detach from parent: + sid = setsid(); + if(sid < 0) + { + qCritical("Could not detach from parent process: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + // Change to some benign directory: + if(chdir("/") < 0) + { + qCritical("Could not change directory: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + // Okay, we are running as a daemon. Defer reopening standard file descriptors + // so we can report errors. + + // Set the file creation mask to allow all processes to connect to + // the socket (access control is handled differently): + umask(0); + + // Ignore SIGPIPE. Connection errors are handled internally. + // According to signal(2), the only error is that the signal number + // is invalid. This cannot happen. + signal(SIGPIPE, SIG_IGN); + + // set up signal handling: + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFds) < 0) + { + qCritical("Could not set up signal handling. Giving up."); + exit(EXIT_FAILURE); + } + socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + QSocketNotifier sigintWatcher(sigintFds[1], QSocketNotifier::Read); + QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &app, SLOT(quit())); + signal(SIGINT, onInterrupt); + + // Create our socket: + int sock = pvsPrivInputMakeServerSocket(); + if(sock < 0) + { + exit(EXIT_FAILURE); + } + + // Our setup is complete. + if(!no_fork) + { + // Reopen standard file descriptors: + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stderr); + freopen("/dev/null", "w", stdout); + } + + // Install our main object + PVSPrivInputHandler handler(sock); + + atexit(unlinkSocket); + + // and run the main loop. + int ret = app.exec(); + + return ret; +} -- cgit v1.2.3-55-g7522 From 9b8657b04122838e3149208789a004baaa3ba635 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:31:11 +0200 Subject: Implement example privileged SayHelloHandler. --- src/input/CMakeLists.txt | 1 + src/input/inputHandlerChain.h | 2 ++ src/input/sayHelloHandler.cpp | 25 +++++++++++++++++++++++++ src/input/sayHelloHandler.h | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 src/input/sayHelloHandler.cpp create mode 100644 src/input/sayHelloHandler.h (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 5f009c5..4aaa845 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -10,6 +10,7 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + sayHelloHandler.cpp ) set(pvsprivinputd_MOC_HDRS diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 61c3d62..57168c2 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -23,6 +23,7 @@ #include "x11FakeKeyboardHandler.h" #include "x11FakeMouseHandler.h" #include "privilegedHandlerForwarder.h" +#include "sayHelloHandler.h" typedef boost::mpl::list< Handler >, @@ -34,6 +35,7 @@ typedef boost::mpl::list< typedef InputEventHandlerChain unprivileged_handler_chain; typedef boost::mpl::list< + Handler >::type privileged_handler_list; typedef InputEventHandlerChain privileged_handler_chain; diff --git a/src/input/sayHelloHandler.cpp b/src/input/sayHelloHandler.cpp new file mode 100644 index 0000000..fc6f668 --- /dev/null +++ b/src/input/sayHelloHandler.cpp @@ -0,0 +1,25 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.h + # - Handle "say hello" requests - interface + # -------------------------------------------------------------------------- + */ + +#include "sayHelloHandler.h" +#include + +using namespace std; + +void SayHelloHandler::handle(InputEvent const&, InputEventContext const* ctx) +{ + cerr << "I'm right here! You sent this message from pid " << ctx->getSenderPid() << " as user " << ctx->getSenderUid() << " with gid " << ctx->getSenderGid() << endl; +} diff --git a/src/input/sayHelloHandler.h b/src/input/sayHelloHandler.h new file mode 100644 index 0000000..b0d4c7e --- /dev/null +++ b/src/input/sayHelloHandler.h @@ -0,0 +1,34 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.h + # - Handle "say hello" requests - interface + # -------------------------------------------------------------------------- + */ + +#ifndef SAYHELLOHANDLER_CPP_ +#define SAYHELLOHANDLER_CPP_ + +#include +#include "inputEventHandler.h" + +class SayHelloHandler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const&, InputEventContext const*); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Say Hello")), InputEvent::ET_SPECIAL, InputEvent::EC_SAY_HELLO); + } +}; + +#endif /* SAYHELLOHANDLER_CPP_ */ -- cgit v1.2.3-55-g7522 From 52585c14017d11c439d4962c1ec12f5b87cc93d6 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:34:12 +0200 Subject: Implement RebootSystem and KillX11 handlers. --- src/input/CMakeLists.txt | 2 + src/input/inputHandlerChain.h | 7 ++- src/input/killX11Handler.cpp | 89 +++++++++++++++++++++++++++++++++++++++ src/input/killX11Handler.h | 34 +++++++++++++++ src/input/rebootSystemHandler.cpp | 32 ++++++++++++++ src/input/rebootSystemHandler.h | 34 +++++++++++++++ 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 src/input/killX11Handler.cpp create mode 100644 src/input/killX11Handler.h create mode 100644 src/input/rebootSystemHandler.cpp create mode 100644 src/input/rebootSystemHandler.h (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 4aaa845..311d8ce 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -10,6 +10,8 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + rebootSystemHandler.cpp + killX11Handler.cpp sayHelloHandler.cpp ) diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 57168c2..8bcb1d8 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -23,7 +23,9 @@ #include "x11FakeKeyboardHandler.h" #include "x11FakeMouseHandler.h" #include "privilegedHandlerForwarder.h" +#include "rebootSystemHandler.h" #include "sayHelloHandler.h" +#include "killX11Handler.h" typedef boost::mpl::list< Handler >, @@ -35,8 +37,11 @@ typedef boost::mpl::list< typedef InputEventHandlerChain unprivileged_handler_chain; typedef boost::mpl::list< - Handler + Handler, + Handler, policy::Security >, + Handler, policy::Security > >::type privileged_handler_list; typedef InputEventHandlerChain privileged_handler_chain; + #endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/killX11Handler.cpp b/src/input/killX11Handler.cpp new file mode 100644 index 0000000..7ac75a1 --- /dev/null +++ b/src/input/killX11Handler.cpp @@ -0,0 +1,89 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # killX11Handler.h + # - Kill the X11 Server - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "pvsCheckPrivileges.h" +#include "killX11Handler.h" + +using namespace std; + +void KillX11Handler::handle(InputEvent const&, InputEventContext const* ctx) +{ + // Find out which display device is used: + QString displayDevice = PVSCheckPrivileges::instance()->getX11DisplayDevice(ctx); + QString displayDeviceAbs = QFileInfo(displayDevice).canonicalFilePath(); + + if(displayDevice.isNull()) + { + qWarning("Can not kill X server for %d,%d,%d: No Display Device", ctx->getSenderPid(), ctx->getSenderUid(), ctx->getSenderGid()); + return; + } + + QList pids; + + // Find all processes that have it opened: + QDir proc("/proc"); + QFileInfoList entries = proc.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach(QFileInfo fi, entries) + { + // We are only interested in numerical ids: + bool ok; + pid_t pid = fi.fileName().toUInt(&ok); + if(!ok) + continue; + + // We have a pid. Now check open files: + QDir fds(QString("/proc/%1/fd").arg(pid)); + qDebug("Searching for X server in %s", fds.absolutePath().toLocal8Bit().constData()); + QFileInfoList fdfiles = fds.entryInfoList(QDir::Files); + foreach(QFileInfo fdfile, fdfiles) + { + qDebug(" Looking for terminal in %s", fdfile.absoluteFilePath().toLocal8Bit().constData()); + QFileInfo fdTarget(fdfile.readLink()); + if(fdTarget.canonicalFilePath() == displayDeviceAbs) + { + qDebug(" ... found"); + pids << pid; + } + } + } + + // If everything is well, we have exactly one pid: + if(pids.size() != 1) + { + QStringList pidStrs; + foreach(pid_t pid, pids) + { + pidStrs << QString::number(pid); + } + + qWarning("Display device %s is open by multiple or zero processes (%s). We don't know which is X. Aborting.", + displayDeviceAbs.toLocal8Bit().constData(), + pidStrs.join(", ").toLocal8Bit().constData()); + return; + } + + // We found the PID for the X server. Now kill it. + QString exe = QFileInfo(QString("/proc/%1/exe").arg(pids[0])).readLink(); + qDebug("Killing X server, PID %d, exe name %s with SIGTERM", pids[0], exe.toLocal8Bit().constData()); + +// kill(pids[0], SIGTERM); +} diff --git a/src/input/killX11Handler.h b/src/input/killX11Handler.h new file mode 100644 index 0000000..2f3ef44 --- /dev/null +++ b/src/input/killX11Handler.h @@ -0,0 +1,34 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # killX11Handler.h + # - Kill the X11 Server - interface + # -------------------------------------------------------------------------- + */ + +#ifndef KILLX11HANDLER_H_ +#define KILLX11HANDLER_H_ + +#include +#include "inputEventHandler.h" + +class KillX11Handler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const&, InputEventContext const*); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Kill X Server")), InputEvent::ET_SPECIAL, InputEvent::EC_KILL_X); + } +}; + +#endif /* KILLX11HANDLER_H_ */ diff --git a/src/input/rebootSystemHandler.cpp b/src/input/rebootSystemHandler.cpp new file mode 100644 index 0000000..b5b8f8a --- /dev/null +++ b/src/input/rebootSystemHandler.cpp @@ -0,0 +1,32 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.cpp + # - Handle system reboot requests - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "rebootSystemHandler.h" + +using namespace std; + +void RebootLinuxSystemHandler::handle(InputEvent const& evt, InputEventContext const* ctx) +{ + // Rebooting a linux system is particulary easy: + if(kill(1, SIGINT) < 0) + { + qWarning("Could not kill /sbin/init: %s", strerror(errno)); + } +} diff --git a/src/input/rebootSystemHandler.h b/src/input/rebootSystemHandler.h new file mode 100644 index 0000000..34fa8ae --- /dev/null +++ b/src/input/rebootSystemHandler.h @@ -0,0 +1,34 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # rebootSystemHandler.h + # - Handle system reboot requests - interface + # -------------------------------------------------------------------------- + */ + +#ifndef REBOOTSYSTEMHANDLER_H_ +#define REBOOTSYSTEMHANDLER_H_ + +#include +#include "inputEventHandler.h" + +class RebootLinuxSystemHandler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const&, InputEventContext const*); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Reboot")), InputEvent::ET_SPECIAL, InputEvent::EC_REBOOT); + } +}; + +#endif /* REBOOTSYSTEMHANDLER_H_ */ -- cgit v1.2.3-55-g7522 From ab5d1fefe041c4083f8e26a436a480abdaa6dae0 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 16:26:57 +0200 Subject: Unify keyword case in src/input/CMakeLists.txt --- src/input/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 311d8ce..29374e2 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -1,6 +1,6 @@ -INCLUDE(${QT_USE_FILE}) +include(${QT_USE_FILE}) -SET(pvsinput_SRCS +set(pvsinput_SRCS inputEvent.cpp inputEventHandler.cpp ) @@ -48,7 +48,7 @@ if(UNIX) ) endif() -ADD_LIBRARY( +add_library( pvsinput STATIC ${pvsinput_SRCS} -- cgit v1.2.3-55-g7522 From 43038c98f8cc48cfded692ea44e02e93597da305 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 19:04:36 +0200 Subject: Add hard requirement for XInput library. XInput2 will be preferred if its presence is detected. --- CMakeLists.txt | 7 +++++- src/input/CMakeLists.txt | 8 ++++++ src/input/x11FakeKeyboardHandler.cpp | 49 +++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) (limited to 'src/input/CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ec688b5..dab8b1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,10 +31,14 @@ INCLUDE_DIRECTORIES( ${CMAKE_BINARY_DIR} ${X11_INCLUDE_DIR} ${X11_XTest_INCLUDE_PATH} + ${X11_Xinput_INCLUDE_PATH} ) IF(NOT X11_XTest_FOUND) - MESSAGE(FATAL_ERROR "Could not find X11 extension XTest. It is needed for PVS") + MESSAGE(FATAL_ERROR "Could not find X11 extension XTest or its developer files.") +ENDIF() +IF(NOT X11_Xinput_FOUND) + MESSAGE(FATAL_ERROR "Could not find X11 extension Xinput or its developer files.") ENDIF() ADD_SUBDIRECTORY(src/input) @@ -322,6 +326,7 @@ TARGET_LINK_LIBRARIES( pvs ${VNC_LIBRARIES} ${X11_LIBRARIES} ${X11_XTest_LIB} + ${X11_Xinput_LIB} pvsinput ) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 29374e2..1a3b154 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -6,6 +6,14 @@ set(pvsinput_SRCS ) if(UNIX) + find_file(XINPUT2_HDR X11/extensions/XInput2.h) + if(XINPUT2_HDR) + set_property(SOURCE x11FakeKeyboardHandler.cpp + APPEND + PROPERTY COMPILE_DEFINITIONS HAVE_XINPUT2_H + ) + endif() + set(pvsprivinputd_SRCS pvsprivinputd.cpp pvsPrivInputHandler.cpp diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index 3a0b864..e7a5826 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -27,7 +27,11 @@ #include #include #include -#include +#ifdef HAVE_XINPUT2_H +# include +#else +# include +#endif #include #include #include "x11InputUtils.h" @@ -457,6 +461,37 @@ modifier_to_keycode_map modifier_to_keycode; typedef std::map keycode_to_modifier_map; keycode_to_modifier_map keycode_to_modifier; +// We need to query the input devices, preferrable through XInput2, but +// if that is not available we will contend ourselves with XInput1: +#ifndef HAVE_XINPUT2_H +# define XIAllDevices 1 /* does not matter */ +# define XIDeviceInfo XDeviceInfo +# define XIQueryDevice(dpy, which, ninfos) XListInputDevices(dpy, ninfos) +# define XIFreeDeviceInfo(infos) XFreeDeviceList(infos) +# define XIMasterKeyboard IsXKeyboard +# define XISlaveKeyboard IsXExtensionKeyboard + static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops) + { + if(devinfo->use == IsXKeyboard) + { + // According to XOpenDevice(3X11) you cannot open the Core Keyboard. + *nprops = 0; + return 0; + } + + XDevice* dev = XOpenDevice(dpy, devinfo->id); + Atom* props = XListDeviceProperties(dpy, dev, nprops); + XCloseDevice(dpy, dev); + return props; + } +#else + static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops) + { + return XIListProperties(dpy, devinfo->deviceid, nprops); + } +#endif + + void initialize_basic_keycodes() { for(int i = 0; i < 8; i++) { @@ -504,23 +539,26 @@ void initialize_basic_keycodes() else { // Try to find out what device XTest will send events on: - int xkbDeviceId = XkbUseCoreKbd; + unsigned int xkbDeviceId = XkbUseCoreKbd; Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false); int ndevinfos; XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); if(devinfos) { +#ifndef HAVE_INPUT2_H +# define deviceid id +#endif for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++) { XIDeviceInfo* devinfo = devinfos + i; - qDebug("Found device of type %d with name %s", devinfo->use, devinfo->name); + qDebug("Found device %lu of type %d with name %s", (unsigned long)devinfo->deviceid, devinfo->use, devinfo->name); // We want it to be a keyboard. if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard) continue; int nprops; - Atom* props = XIListProperties(dpy, devinfo->deviceid, &nprops); + Atom* props = getDeviceProperties(dpy, devinfo, &nprops); if(props) { for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++) @@ -536,6 +574,9 @@ void initialize_basic_keycodes() } } XIFreeDeviceInfo(devinfos); +#ifdef deviceid /* XInput1 */ +# undef deviceid +#endif } // Okay, we know which device to query. Now get its keymap: -- cgit v1.2.3-55-g7522 From 9602e1063f5459d9ca05ba8f90304b7d34072561 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:01:33 +0200 Subject: Implement administratively configured user privileges The administrator can set a list of users and a list of groups to see as privileged. This list is reloaded whenever the configuration file changes, or changes to the user/group database are detected. --- src/input/CMakeLists.txt | 1 + src/input/pvsCheckPrivileges.cpp | 161 ++++++++++++++++++++++++++++++++++++++- src/input/pvsCheckPrivileges.h | 19 ++++- 3 files changed, 178 insertions(+), 3 deletions(-) (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 1a3b154..29c463a 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -25,6 +25,7 @@ if(UNIX) set(pvsprivinputd_MOC_HDRS pvsPrivInputHandler.h + pvsCheckPrivileges.h ) qt4_wrap_cpp(pvsprivinputd_MOC_SRCS diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index 56073bc..71c5861 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -19,13 +19,19 @@ # -------------------------------------------------------------------------- */ +#include +#include +#include #include #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -35,6 +41,7 @@ #include #include #include +#include "pvsPrivInputSocket.h" #include "pvsCheckPrivileges.h" using namespace std; @@ -97,8 +104,19 @@ PVSCheckPrivileges* PVSCheckPrivileges::instance() return &static_instance; } -PVSCheckPrivileges::PVSCheckPrivileges() +PVSCheckPrivileges::PVSCheckPrivileges(QObject* parent) + : QObject(parent) { + // initialise our cache: + updateCachedUserDatabase(); + rereadConfiguration(); + + // and make it update itself: + QStringList paths; + paths << "/etc/passwd" << "/etc/group" << pvsPrivInputGetSettingsPath(); + _watcher = new QFileSystemWatcher(paths, this); + connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(updateCachedUserDatabase())); + connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(rereadConfiguration())); } PVSCheckPrivileges::~PVSCheckPrivileges() @@ -164,8 +182,26 @@ PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInp { // Always allow root: if(sender.uid == 0) + { + return USER_PRIVILEGED; + } + + // Or if the user is one of those enumerated in the privileged-users configuration value: + if(_privilegedUsers.contains(sender.uid)) + { return USER_PRIVILEGED; + } + + // Or if the user is a member of one of the groups enumerated in the privileged-groups configuration value: + foreach(gid_t gid, _privilegedGroups) + { + if(_userGroupMap.contains(sender.uid, gid)) + { + return USER_PRIVILEGED; + } + } + // User is not trivially privileged, so try to check with PolicyKit. // 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); @@ -312,3 +348,126 @@ uint qHash(CachedInputContext const& p) { return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid))); } + +void PVSCheckPrivileges::updateCachedUserDatabase() +{ + QHash userNames; + + _userGroupMap.clear(); + + // assemble a list of known users and their primary groups: + setpwent(); // open the user database + struct passwd* userrec; + while((userrec = getpwent())) + { + userNames.insert(userrec->pw_name, userrec->pw_uid); + _userGroupMap.insert(userrec->pw_uid, userrec->pw_gid); + } + endpwent(); // close the database + + // augment with secondary groups: + setgrent(); // open the group database + struct group* grouprec; + while((grouprec = getgrent())) + { + char** membername = grouprec->gr_mem; + while(*membername) + { + uid_t uid = userNames.value(*membername, (uid_t)-1); + if(uid != (uid_t)-1) + { + _userGroupMap.insert(uid, grouprec->gr_gid); + } + membername++; + } + } + endgrent(); + + // decisions may have changed, so clear the caches: + _savedUserPrivilege.clear(); +} + +void PVSCheckPrivileges::rereadConfiguration() +{ + QSettings* settings = pvsPrivInputReopenSettings(); + + _privilegedGroups.clear(); + QVariant groupList = settings->value("privileged-groups"); + if(groupList.isValid()) + { + if(!groupList.canConvert(QVariant::StringList)) + { + qWarning("There is a privileged-groups setting, but it cannot be converted to a list of strings."); + goto endGroupListScan; + } + + QStringList groups = groupList.toStringList(); + foreach(QString groupName, groups) + { + bool ok; + gid_t gid = groupName.toUInt(&ok); + if(ok) + { + _privilegedGroups.append(gid); + } + else + { + // lookup the name: + QByteArray groupNameBytes = groupName.toLocal8Bit(); + struct group* group = getgrnam(groupNameBytes.constData()); + if(group) + { + _privilegedGroups.append(group->gr_gid); + } + else + { + qWarning("privileged-groups setting contains %s which is neither a numeric GID " + "nor a valid group name. Skipping.", + groupNameBytes.constData()); + } + } + } + } + +endGroupListScan: + _privilegedUsers.clear(); + QVariant userList = settings->value("privileged-users"); + if(userList.isValid()) + { + if(!userList.canConvert(QVariant::StringList)) + { + qWarning("There is a privileged-users setting, but it cannot be converted to a list of strings."); + goto endUserListScan; + } + + QStringList users = userList.toStringList(); + foreach(QString userName, users) + { + bool ok; + uid_t uid = userName.toUInt(&ok); + if(ok) + { + _privilegedUsers.append(uid); + } + else + { + // lookup the name: + QByteArray userNameBytes = userName.toLocal8Bit(); + struct passwd* user = getpwnam(userNameBytes.constData()); + if(user) + { + _privilegedUsers.append(user->pw_uid); + } + else + { + qWarning("privileged-users setting contains %s which is neither a numeric UID " + "nor a valid user name. Skipping.", + userNameBytes.constData()); + } + } + } + } + +endUserListScan: + return; +} diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h index e5cc9a4..fc82e4c 100644 --- a/src/input/pvsCheckPrivileges.h +++ b/src/input/pvsCheckPrivileges.h @@ -19,7 +19,10 @@ #define PVSCHECKPRIVILEGES_H_ #include +#include #include +#include +#include #include #include "inputEventHandler.h" @@ -64,8 +67,11 @@ struct CachedInputContext }; uint qHash(CachedInputContext const& p); -class PVSCheckPrivileges +class QFileSystemWatcher; + +class PVSCheckPrivileges : public QObject { + Q_OBJECT public: typedef enum { SESSION_LOOKUP_FAILURE, // Comes first because we default to assume @@ -115,8 +121,12 @@ public: QString getX11SessionName(CachedInputContext const& sender); QString getX11DisplayDevice(CachedInputContext const& sender); +public slots: + void updateCachedUserDatabase(); + void rereadConfiguration(); + private: - PVSCheckPrivileges(); + PVSCheckPrivileges(QObject* parent = 0); virtual ~PVSCheckPrivileges(); typedef QPair > piduidgid; @@ -134,6 +144,11 @@ private: QHash _savedUserPrivilege; QHash _savedSessionKind; QHash _savedConsoleKitSession; + + QList _privilegedUsers; + QList _privilegedGroups; + QMultiMap _userGroupMap; + QFileSystemWatcher* _watcher; }; #endif /* PVSCHECKPRIVILEGES_H_ */ -- cgit v1.2.3-55-g7522 From 6afd810d1954018791456a7cebca0d275c50ed95 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:08:03 +0200 Subject: Refactor signal handling in pvsprivinputd Only use one socketpair and delegate the actual decision of what to do when a specific signal is received to a special object. --- src/input/CMakeLists.txt | 2 + src/input/pvsPrivInputSignalHandler.cpp | 69 +++++++++++++++++++++++++++++++++ src/input/pvsPrivInputSignalHandler.h | 47 ++++++++++++++++++++++ src/input/pvsPrivInputSocket.cpp | 14 ++++++- src/input/pvsPrivInputSocket.h | 1 + src/input/pvsprivinputd.cpp | 40 +++++++++++-------- 6 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 src/input/pvsPrivInputSignalHandler.cpp create mode 100644 src/input/pvsPrivInputSignalHandler.h (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 29c463a..fcb5ff5 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -18,6 +18,7 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + pvsPrivInputSignalHandler.cpp rebootSystemHandler.cpp killX11Handler.cpp sayHelloHandler.cpp @@ -26,6 +27,7 @@ if(UNIX) set(pvsprivinputd_MOC_HDRS pvsPrivInputHandler.h pvsCheckPrivileges.h + pvsPrivInputSignalHandler.h ) qt4_wrap_cpp(pvsprivinputd_MOC_SRCS diff --git a/src/input/pvsPrivInputSignalHandler.cpp b/src/input/pvsPrivInputSignalHandler.cpp new file mode 100644 index 0000000..b09c335 --- /dev/null +++ b/src/input/pvsPrivInputSignalHandler.cpp @@ -0,0 +1,69 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsPrivInputSignalHandler.cpp: + # - Handle signals forwarded over a file descriptor - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "pvsPrivInputSocket.h" +#include "pvsPrivInputSignalHandler.h" + +void PVSPrivInputSignalHandler::signalReceived(int sigfd) +{ + int signum; + + pid_t pid; + uid_t uid; + gid_t gid; + int err; + size_t siz = sizeof(signum); + + if(!pvsPrivInputRecvMessage(sigfd, &signum, siz, pid, uid, gid, &err)) + { + qWarning("Should have received a signal but could not read message: %s", strerror(err)); + return; + } + + if(siz != sizeof(signum)) + { + qWarning("Sould have received a message of size %d bytes but got %d bytes instead. Discarding message.", (int)sizeof(signum), (int)siz); + return; + } + + if(!_allowUnauthenticatedKilling && pid != getpid()) + { + qCritical("SOMETHING STRANGE IS GOING ON!\nReceived signal %d from PID %d\nPlease kill me instead of sending me signal notifications.", signum, (int)pid); + return; + } + + switch(signum) + { + case SIGINT: + qDebug("Caught SIGINT. Quitting."); + emit terminate(); + break; + case SIGTERM: + qDebug("Caught SIGTERM. Quitting."); + emit terminate(); + break; + case SIGHUP: + qDebug("Caught SIGHUP. Reloading configuration."); + emit reloadConfiguration(); + break; + default: + qWarning("Received signal %d, but don't know how to handle it.", signum); + } +} diff --git a/src/input/pvsPrivInputSignalHandler.h b/src/input/pvsPrivInputSignalHandler.h new file mode 100644 index 0000000..cb75c86 --- /dev/null +++ b/src/input/pvsPrivInputSignalHandler.h @@ -0,0 +1,47 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsPrivInputSignalHandler.h: + # - Handle signals forwarded over a file descriptor - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSPRIVINPUTSIGNALHANDLER_H_ +#define PVSPRIVINPUTSIGNALHANDLER_H_ + +#include + +class PVSPrivInputSignalHandler : public QObject +{ + Q_OBJECT +public: + PVSPrivInputSignalHandler(QObject* parent = 0) + : QObject(parent) + { + } + + void allowUnauthenticatedKilling(bool value) + { + _allowUnauthenticatedKilling = value; + } + +public slots: + void signalReceived(int sigfd); + +signals: + void terminate(); + void reloadConfiguration(); + +private: + bool _allowUnauthenticatedKilling; +}; + +#endif /* PVSPRIVINPUTSIGNALHANDLER_H_ */ diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp index c491dd9..df5dff5 100644 --- a/src/input/pvsPrivInputSocket.cpp +++ b/src/input/pvsPrivInputSocket.cpp @@ -60,6 +60,17 @@ QString pvsPrivInputGetSocketAddress() return pvsPrivInputGetSettings()->value("socketpath", "/tmp/pvsprivinputd.sock").toString(); } +bool pvsPrivInputEnableReceiveCredentials(int sock) +{ + int passcred = 1; + if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0) + { + return false; + } + else + { + return true; + } } int pvsPrivInputMakeClientSocket() @@ -110,8 +121,7 @@ int pvsPrivInputMakeServerSocket() } // Announce that credentials are requested: - int passcred = 1; - if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0) + if(!pvsPrivInputEnableReceiveCredentials(sock)) { // We will not operate without credentials. qCritical("Could not request peer credentials: %s", strerror(errno)); diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h index 879ca99..447360b 100644 --- a/src/input/pvsPrivInputSocket.h +++ b/src/input/pvsPrivInputSocket.h @@ -28,6 +28,7 @@ QSettings* pvsPrivInputGetSettings(); QSettings* pvsPrivInputReopenSettings(); QString pvsPrivInputGetSettingsPath(); QString pvsPrivInputGetSocketAddress(); +bool pvsPrivInputEnableReceiveCredentials(int sock); int pvsPrivInputMakeClientSocket(); int pvsPrivInputMakeServerSocket(); bool pvsPrivInputSendMessage(int sock, void* buf, size_t len, int* err = 0); diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp index 3a5e1a8..361bf9f 100644 --- a/src/input/pvsprivinputd.cpp +++ b/src/input/pvsprivinputd.cpp @@ -30,6 +30,7 @@ #include #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" +#include "pvsPrivInputSignalHandler.h" using namespace std; @@ -38,7 +39,7 @@ using namespace std; #endif QByteArray socketPath; -int sigintFds[2]; +int signalFds[2]; static void unlinkSocket() { @@ -48,12 +49,9 @@ static void unlinkSocket() } } -static void onInterrupt(int) +static void onCaughtSignal(int signum) { - char buf[] = { '!' }; - char msg[] = "SIGINT caught. Quitting.\n"; - write(sigintFds[0], buf, 1); - write(1, msg, sizeof(msg)-1); + pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0); } int main(int argc, char** argv) @@ -103,28 +101,38 @@ int main(int argc, char** argv) // the socket (access control is handled differently): umask(0); + // Store the socket path before the signal handler is installed so it does not risk segfaulting + // due to bad timing. + socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); + // Ignore SIGPIPE. Connection errors are handled internally. // According to signal(2), the only error is that the signal number // is invalid. This cannot happen. signal(SIGPIPE, SIG_IGN); - // set up signal handling: - if(socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFds) < 0) + // Create our socket: + int sock = pvsPrivInputMakeServerSocket(); + if(sock < 0) { - qCritical("Could not set up signal handling. Giving up."); exit(EXIT_FAILURE); } - socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit(); - QSocketNotifier sigintWatcher(sigintFds[1], QSocketNotifier::Read); - QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &app, SLOT(quit())); - signal(SIGINT, onInterrupt); - // Create our socket: - int sock = pvsPrivInputMakeServerSocket(); - if(sock < 0) + // set up signal handling: + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0) { + qCritical("Could not set up signal handling. Giving up."); exit(EXIT_FAILURE); } + PVSPrivInputSignalHandler sigHandler; + QSocketNotifier sigintWatcher(signalFds[1], QSocketNotifier::Read); + sigHandler.allowUnauthenticatedKilling(!pvsPrivInputEnableReceiveCredentials(signalFds[1])); + QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &sigHandler, SLOT(signalReceived(int))); + QObject::connect(&sigHandler, SIGNAL(terminate()), &app, SLOT(quit())); + QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(updateCachedUserDatabase())); + QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(rereadConfiguration())); + signal(SIGINT, onCaughtSignal); + signal(SIGTERM, onCaughtSignal); + signal(SIGHUP, onCaughtSignal); // Our setup is complete. if(!no_fork) -- cgit v1.2.3-55-g7522 From c8579de3b79b64bcb72adfd97d2ebf424e8df4b7 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:13:31 +0200 Subject: Detect the presence of PolKit on configuration When no PolKit is installed, the program gets built without support for it. This can be overriden by specifying -DENABLE_POLKIT=ON on the cmake command line. --- src/input/CMakeLists.txt | 21 +++++++++++++++++++++ src/input/pvsCheckPrivileges.cpp | 4 ++++ 2 files changed, 25 insertions(+) (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index fcb5ff5..44cf6b6 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -57,6 +57,27 @@ if(UNIX) x11FakeMouseHandler.cpp privilegedHandlerForwarder.cpp ) + + # we need pkg-config to find out where to install the action file: + find_package(PkgConfig) + if(NOT PKG_CONFIG_FOUND) + # we will try to make a best effort and put our policy file into + # ${PREFIX}/share/polkit-1, but only if we can find + # the pkexec executable. + find_program(PKEXEC_LOCATION pkexec) + if(PKEXEC_LOCATION OR ENABLE_POLKIT) + set(POLKIT_FOUND ON) + endif() + else() + pkg_check_modules(POLKIT "polkit-gobject-1") + endif() + + # now, arrange for policykit integration: + if(POLKIT_FOUND OR ENABLE_POLKIT) + set_property(SOURCE ${pvsprivinputd_SRCS} + APPEND + PROPERTY COMPILE_DEFINITIONS HAVE_POLKIT) + endif() endif() add_library( diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index 57b5d8b..f9a8851 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -211,6 +211,7 @@ PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInp } // User is not trivially privileged, so try to check with PolicyKit. +#ifdef HAVE_POLKIT // but only if it is present // 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); @@ -257,6 +258,9 @@ PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInp return USER_LOOKUP_FAILURE; } return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED; +#else + return USER_UNPRIVILEGED; +#endif } QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender) -- cgit v1.2.3-55-g7522 From dce35518d9b91b1bac0394792f83450fbe4aa3bc Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:17:40 +0200 Subject: Install PolKit policy file and try to detect the policy directory. --- src/input/CMakeLists.txt | 19 +++++++++++++++++++ src/input/org.openslx.pvs.input.policy | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/input/org.openslx.pvs.input.policy (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 44cf6b6..0cf5177 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -67,16 +67,35 @@ if(UNIX) find_program(PKEXEC_LOCATION pkexec) if(PKEXEC_LOCATION OR ENABLE_POLKIT) set(POLKIT_FOUND ON) + set(POLKIT_PREFIX ${CMAKE_INSTALL_PREFIX}) + set(POLKIT_POLICY_DIR ${POLKIT_PREFIX}/share/polkit-1/actions) endif() else() pkg_check_modules(POLKIT "polkit-gobject-1") + if(POLKIT_FOUND) + execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} polkit-gobject-1 --variable=policydir + OUTPUT_VARIABLE POLKIT_POLICY_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT POLKIT_POLICY_DIR) + set(POLKIT_FOUND OFF) + endif() + endif() endif() # now, arrange for policykit integration: if(POLKIT_FOUND OR ENABLE_POLKIT) + if(NOT POLKIT_POLICY_DIR) + message(SEND_ERROR "PolicyKit integration is enabled, but cannot find PolicyKit's actions directory. Please set POLKIT_POLICY_DIR to the right value") + endif() + + install(FILES org.openslx.pvs.input.policy + DESTINATION ${POLKIT_POLICY_DIR}) set_property(SOURCE ${pvsprivinputd_SRCS} APPEND PROPERTY COMPILE_DEFINITIONS HAVE_POLKIT) + message(STATUS "PolicyKit integration: enabled") + else() + message(STATUS "PolicyKit integration: disabled") endif() endif() diff --git a/src/input/org.openslx.pvs.input.policy b/src/input/org.openslx.pvs.input.policy new file mode 100644 index 0000000..f0de9f2 --- /dev/null +++ b/src/input/org.openslx.pvs.input.policy @@ -0,0 +1,18 @@ + + + + The OpenSLX project + http://lab.openslx.org/pvs + + + Use privileged input actions in PVS + Authentication is required to let PVS use privileged input actions + + auth_self_keep + auth_self_keep + auth_admin_keep + + + -- cgit v1.2.3-55-g7522 From aeeb2b034f50ad2938b1c32507f20070a26c1849 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:19:44 +0200 Subject: Implement log targets for pvsprivinputd Also: - add command line options and usage message. - change --no-fork to --daemon to bring it in line with the pvs daemon. --- src/input/CMakeLists.txt | 2 + src/input/pvsSyslog.cpp | 133 ++++++++++++++++++++++++++++++++++++++++++++ src/input/pvsSyslog.h | 63 +++++++++++++++++++++ src/input/pvsprivinputd.cpp | 116 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 308 insertions(+), 6 deletions(-) create mode 100644 src/input/pvsSyslog.cpp create mode 100644 src/input/pvsSyslog.h (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 0cf5177..a2e6fda 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -18,6 +18,7 @@ if(UNIX) pvsprivinputd.cpp pvsPrivInputHandler.cpp pvsCheckPrivileges.cpp + pvsSyslog.cpp pvsPrivInputSignalHandler.cpp rebootSystemHandler.cpp killX11Handler.cpp @@ -27,6 +28,7 @@ if(UNIX) set(pvsprivinputd_MOC_HDRS pvsPrivInputHandler.h pvsCheckPrivileges.h + pvsSyslog.h pvsPrivInputSignalHandler.h ) diff --git a/src/input/pvsSyslog.cpp b/src/input/pvsSyslog.cpp new file mode 100644 index 0000000..6a9be31 --- /dev/null +++ b/src/input/pvsSyslog.cpp @@ -0,0 +1,133 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsSyslog.cpp: + # - Send output to syslog or to a file - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pvsSyslog.h" + +using namespace std; + +#define PVS_LOG_BUFSIZ 8192 + +void PVSLogRedirector::inputAvailableOn(int fd) +{ + QByteArray readbuf; + readbuf.resize(PVS_LOG_BUFSIZ); + ssize_t siz; + bool cont = true; + + do + { + siz = recv(fd, readbuf.data(), readbuf.size(), MSG_DONTWAIT); + if(siz > 0) + { + _buf.append(readbuf.constData(), siz); + + // Send lines: + int oldnpos = -1; + int npos = -1; + while((npos = _buf.indexOf('\n', oldnpos + 1)) >= 0) + { + if((oldnpos + 1) == npos) + { + oldnpos = npos; + continue; + } + QString line = QString::fromLocal8Bit(_buf.constData() + oldnpos + 1, npos - oldnpos - 1); + doRedirectInput(line); + oldnpos = npos; + } + + // All complete lines have been sent. Remove sent bytes from buffer. + _buf.remove(0, oldnpos + 1); + + // If line length is too large, send it now. + if(_buf.size() >= BUFSIZ) + { + QString longLine = QString::fromLocal8Bit(_buf.constData(), _buf.size()); + doRedirectInput(longLine); + _buf.clear(); + } + } + else if(siz == 0) + { + // Socket closed on other end. + break; + } + else if(siz < 0) + { + int err = errno; + switch(err) + { + case EAGAIN: +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: +#endif + // We read all there is to read. + cont = false; + break; + default: + // Something happened. + // We can't inform the user via qDebug() and friends as that would loop right back to us again, + // so we emit a log line. + doRedirectInput(QString("Error in logging code: Could not read log input: %s").arg(strerror(err))); + cont = false; + } + } + } + while(cont); +} + +PVSSyslogRedirector::PVSSyslogRedirector() +{ + openlog("pvsprivinputd", LOG_PID, LOG_USER); +} + +PVSSyslogRedirector::~PVSSyslogRedirector() +{ + closelog(); +} + +void PVSSyslogRedirector::doRedirectInput(QString const& line) +{ + QByteArray bytes = line.toLocal8Bit(); + syslog(LOG_NOTICE, "%s", bytes.data()); +} + +PVSLogfileRedirector::PVSLogfileRedirector(QString const& filename) +{ + _file = new QFile(filename, this); + _file->open(QIODevice::Append); + _stream = new QTextStream(_file); +} + +PVSLogfileRedirector::~PVSLogfileRedirector() +{ + _stream->flush(); + _file->close(); + delete _stream; + delete _file; +} + +void PVSLogfileRedirector::doRedirectInput(QString const& line) +{ + *_stream << line << flush; +} diff --git a/src/input/pvsSyslog.h b/src/input/pvsSyslog.h new file mode 100644 index 0000000..8c9591a --- /dev/null +++ b/src/input/pvsSyslog.h @@ -0,0 +1,63 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # pvsSyslog.h: + # - Send output to syslog or to a file - interface + # -------------------------------------------------------------------------- + */ + +#ifndef PVSSYSLOG_H_ +#define PVSSYSLOG_H_ + +#include +#include + +class PVSLogRedirector : public QObject +{ + Q_OBJECT +public slots: + void inputAvailableOn(int fd); + +protected: + virtual void doRedirectInput(QString const& line) = 0; + +private: + QByteArray _buf; +}; + +class PVSSyslogRedirector : public PVSLogRedirector +{ +public: + PVSSyslogRedirector(); + virtual ~PVSSyslogRedirector(); + +protected: + void doRedirectInput(QString const& line); +}; + +class QFile; +class QTextStream; + +class PVSLogfileRedirector : public PVSLogRedirector +{ +public: + PVSLogfileRedirector(QString const& filename); + virtual ~PVSLogfileRedirector(); + +protected: + void doRedirectInput(QString const& line); + +private: + QFile* _file; + QTextStream* _stream; +}; + +#endif /* PVSSYSLOG_H_ */ diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp index 2f19d90..e6ae155 100644 --- a/src/input/pvsprivinputd.cpp +++ b/src/input/pvsprivinputd.cpp @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -32,6 +34,7 @@ #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" #include "pvsPrivInputSignalHandler.h" +#include "pvsSyslog.h" using namespace std; @@ -42,6 +45,9 @@ using namespace std; QByteArray socketPath; int signalFds[2]; +QTextStream qout(stdout, QIODevice::WriteOnly); +QTextStream qerr(stderr, QIODevice::WriteOnly); + static void unlinkSocket() { if(!socketPath.isNull()) @@ -55,13 +61,70 @@ static void onCaughtSignal(int signum) pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0); } +static void usage() +{ + qout << QObject::tr( + "Usage: %1 [--help|-h] [--daemon|-d] [--log=|-l]\n" + "\n" + "Options:\n" + " --help, -h Show this message\n" + " --daemon, -d Run in background\n" + " --log=,\n" + " -l Redirect all output to \n" + " valid values are:\n" + " - any file name\n" + " - `syslog' to redirect output to the system log\n" + " - `null' to discard output (default)\n" + " (without quotes)\n" + "\n" + "Signals:\n" + " SIGINT, SIGTERM (or press Ctrl+C when run in foreground)\n" + " Quit\n" + " SIGHUP Reload configuration and cached user data\n").arg(qApp->arguments().at(0)) << flush; +} + int main(int argc, char** argv) { QCoreApplication app(argc, argv); app.setApplicationName("pvsprivinputd"); app.setOrganizationName("openslx"); - bool no_fork = app.arguments().contains("--no-fork", Qt::CaseInsensitive); + bool no_fork = true; + QString logTarget = "null"; + + foreach(QString arg, app.arguments().mid(1)) + { + if(arg == "--daemon" || arg == "-d") + { + no_fork = false; + } + else if(arg.startsWith("-l")) + { + logTarget = arg.mid(2); + } + else if(arg.startsWith("--log=")) + { + logTarget = arg.mid(6); + } + else if(arg == "--help" || arg == "-h") + { + usage(); + return EXIT_SUCCESS; + } + else + { + qout << "Unexpected argument: " << arg << endl; + usage(); + return EXIT_FAILURE; + } + } + + if(logTarget != "null" && logTarget != "syslog") + { + logTarget = QFileInfo(logTarget).absoluteFilePath(); + qout << "Writing log to " << logTarget << endl; + } + if(!no_fork) { pid_t pid; @@ -118,6 +181,9 @@ int main(int argc, char** argv) exit(EXIT_FAILURE); } + // Install our main object + PVSPrivInputHandler handler(sock); + // set up signal handling: if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0) { @@ -135,24 +201,62 @@ int main(int argc, char** argv) signal(SIGTERM, onCaughtSignal); signal(SIGHUP, onCaughtSignal); + PVSLogRedirector* logRedir = 0; + QSocketNotifier* logNotif = 0; // Our setup is complete. if(!no_fork) { // Reopen standard file descriptors: freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stderr); - freopen("/dev/null", "w", stdout); - } - // Install our main object - PVSPrivInputHandler handler(sock); + if(logTarget == "null") + { + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + } + else if(logTarget == "syslog") + { + logRedir = new PVSSyslogRedirector(); + } + else + { + logRedir = new PVSLogfileRedirector(logTarget); + } + + if(logRedir) + { + int logFds[2]; + if(socketpair(AF_UNIX, SOCK_STREAM, 0, logFds) < 0) + { + qWarning("Could not open a socket pair: %s", strerror(errno)); + return EXIT_FAILURE; + } + + logNotif = new QSocketNotifier(logFds[1], QSocketNotifier::Read); + QObject::connect(logNotif, SIGNAL(activated(int)), logRedir, SLOT(inputAvailableOn(int))); + + // redirect stdout and stderr: + dup2(logFds[0], 1); + dup2(logFds[0], 2); + } + } atexit(unlinkSocket); + qout << "PVS Privileged Input Daemon initialization complete. Entering main loop." << endl; + // and run the main loop. int ret = app.exec(); + if(logRedir) + { + delete logNotif; + delete logRedir; + } PVSCheckPrivileges::deleteInstance(); + + qout << "PVS Privileged Input Daemon deinitialization complete. Exiting." << endl; + return ret; } -- cgit v1.2.3-55-g7522 From ea97a3d4459e856c62db281a0f12e20452aebefc Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:21:49 +0200 Subject: Install a pvsprivinputd.conf template into /etc --- src/input/CMakeLists.txt | 5 +++++ src/input/pvsprivinputd.conf | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/input/pvsprivinputd.conf (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index a2e6fda..398ca55 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -99,6 +99,11 @@ if(UNIX) else() message(STATUS "PolicyKit integration: disabled") endif() + + # Install a pvsprivinputd.conf template + install(FILES pvsprivinputd.conf + DESTINATION /etc + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) endif() add_library( diff --git a/src/input/pvsprivinputd.conf b/src/input/pvsprivinputd.conf new file mode 100644 index 0000000..52df206 --- /dev/null +++ b/src/input/pvsprivinputd.conf @@ -0,0 +1,14 @@ +;; Lines starting with `;' are comments. +;; +;; socketpath: +;; Where pvsprivinputd should put its listening socket. +socketpath = /tmp/pvsprivinputd.sock + +;; privileged-users: +;; Comma-separated list of users that are allowed to run privileged actions +privileged-users = root + +;; privileged-groups: +;; Comma-separated list of user groups that are allowed to run privileged actions +; privileged-groups = wheel + \ No newline at end of file -- cgit v1.2.3-55-g7522 From c5a99933202c91630edc2ddd97e0e964b27540d6 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 17:56:59 +0200 Subject: Sanitize security model yet again The flags model was not satisfactory since it made it unnecessarily difficult to express the standard policy of "allow all to users that are physically sitting in front of the machine and to privileged users". The new model expressly knows different policies (two at the moment) and refrains from decomposing them. Additional policies are not difficult to add. --- src/input/CMakeLists.txt | 2 +- src/input/inputEventHandler.h | 58 +++++++++++++++++++++++++++++++------------ src/input/inputHandlerChain.h | 10 ++++---- 3 files changed, 48 insertions(+), 22 deletions(-) (limited to 'src/input/CMakeLists.txt') diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 398ca55..0e72c4c 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -2,7 +2,6 @@ include(${QT_USE_FILE}) set(pvsinput_SRCS inputEvent.cpp - inputEventHandler.cpp ) if(UNIX) @@ -23,6 +22,7 @@ if(UNIX) rebootSystemHandler.cpp killX11Handler.cpp sayHelloHandler.cpp + inputEventHandler.cpp ) set(pvsprivinputd_MOC_HDRS diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 44713c2..52e3338 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -18,6 +18,7 @@ #define INPUTEVENTHANDLER_H_ #include +#include #include #include #include @@ -97,26 +98,33 @@ public: namespace policy { enum SecurityFlags { - SEC_PHYSICAL_SEAT = 1, - SEC_PRIVILEGED_USER = 2 + SEC_FREE_FOR_ALL, + SEC_PHYSICAL_OR_PRIVILEGED }; bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx); bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx); -template -struct Security +struct SecurityAllowAny { 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; } }; +struct SecurityAllowPhysicalOrPrivileged +{ + bool allow(InputEvent const& evt, InputEventContext const* ctx) + { + if(allowPhysicalSeat(evt, ctx)) + return true; + else if(allowPrivilegedUser(evt, ctx)) + return true; + return false; + } +}; + struct UnixLike; struct Linux; struct Windows; @@ -154,6 +162,8 @@ public: bool handle(InputEvent const& evt, InputEventContext const* context = 0) { if(!securityPolicy.allow(evt, context)) { + std::string evtStr = evt.toString(); + qWarning("Input Event %s has been denied by security policy", evtStr.c_str()); return true; } if(delegate.matches(evt, context)) { @@ -196,19 +206,32 @@ public: } }; -template > +template struct Handler : public HandlerHelper { }; -template +template +struct ApplyDefaultSecurityPolicy +{ + typedef HandlerType type; +}; + +template +struct ApplyDefaultSecurityPolicy > +{ + typedef Handler type; +}; + +template struct InputEventHandlerChainHelper { private: typedef typename boost::mpl::next::type next_iterator_type; - typedef InputEventHandlerChainHelper next_in_chain; + typedef InputEventHandlerChainHelper next_in_chain; - typedef typename boost::mpl::deref::type handler_type; + typedef typename boost::mpl::deref::type handler_entry_type; + typedef typename ApplyDefaultSecurityPolicy::type handler_type; handler_type _handler; next_in_chain _next; @@ -239,8 +262,8 @@ public: } }; -template -struct InputEventHandlerChainHelper +template +struct InputEventHandlerChainHelper { void handle(InputEvent const&, InputEventContext const* context = 0) { // do nothing @@ -261,8 +284,11 @@ struct InputEventHandlerChainHelper } }; -template -struct InputEventHandlerChain : public InputEventHandlerChainHelper::type, typename boost::mpl::end::type> +template +struct InputEventHandlerChain : + public InputEventHandlerChainHelper::type, + typename boost::mpl::end::type> { }; diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index 8bcb1d8..b012aa6 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -34,14 +34,14 @@ typedef boost::mpl::list< Handler >::type unprivileged_handler_list; -typedef InputEventHandlerChain unprivileged_handler_chain; +typedef InputEventHandlerChain unprivileged_handler_chain; typedef boost::mpl::list< - Handler, - Handler, policy::Security >, - Handler, policy::Security > + Handler, + Handler >, + Handler > >::type privileged_handler_list; -typedef InputEventHandlerChain privileged_handler_chain; +typedef InputEventHandlerChain privileged_handler_chain; #endif /* INPUTHANDLERCHAIN_H_ */ -- cgit v1.2.3-55-g7522 From 32672a51fe8b8ccf63e1cfeb89ac5e020fde787a Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 18:00:08 +0200 Subject: Implement magic SysRq handler. --- CMakeLists.txt | 1 + src/input/CMakeLists.txt | 1 + src/input/inputHandlerChain.h | 4 ++- src/input/magicSysRqHandler.cpp | 28 ++++++++++++++++ src/input/magicSysRqHandler.h | 72 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/input/magicSysRqHandler.cpp create mode 100644 src/input/magicSysRqHandler.h (limited to 'src/input/CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 85fb968..cc695ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ SET( PVSMGR_SRCS src/input/killX11Handler.h src/input/rebootSystemHandler.h src/input/sayHelloHandler.h + src/input/magicSysRqHandler.h ) # pvs diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 0e72c4c..20fd531 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -21,6 +21,7 @@ if(UNIX) pvsPrivInputSignalHandler.cpp rebootSystemHandler.cpp killX11Handler.cpp + magicSysRqHandler.cpp sayHelloHandler.cpp inputEventHandler.cpp ) diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h index b012aa6..3c9446c 100644 --- a/src/input/inputHandlerChain.h +++ b/src/input/inputHandlerChain.h @@ -26,6 +26,7 @@ #include "rebootSystemHandler.h" #include "sayHelloHandler.h" #include "killX11Handler.h" +#include "magicSysRqHandler.h" typedef boost::mpl::list< Handler >, @@ -39,7 +40,8 @@ typedef InputEventHandlerChain, Handler >, - Handler > + Handler >, + Handler > >::type privileged_handler_list; typedef InputEventHandlerChain privileged_handler_chain; diff --git a/src/input/magicSysRqHandler.cpp b/src/input/magicSysRqHandler.cpp new file mode 100644 index 0000000..108bfca --- /dev/null +++ b/src/input/magicSysRqHandler.cpp @@ -0,0 +1,28 @@ +/* + # 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 + # -------------------------------------------------------------------------- + */ + +#include +#include "magicSysRqHandler.h" + +void MagicSysRqHandler::handle(InputEvent const& evt, InputEventContext const*) +{ + QFile trigger("/proc/sysrq-trigger"); + trigger.open(QIODevice::WriteOnly); + char c = (char)(evt.value() & 0xff); + trigger.write(&c, 1); + trigger.flush(); + trigger.close(); +} diff --git a/src/input/magicSysRqHandler.h b/src/input/magicSysRqHandler.h new file mode 100644 index 0000000..563d091 --- /dev/null +++ b/src/input/magicSysRqHandler.h @@ -0,0 +1,72 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # magicSysRqHandler.h + # - Trigger Magic-SysRq functions - interface + # -------------------------------------------------------------------------- + */ + +#ifndef MAGICSYSRQHANDLER_H_ +#define MAGICSYSRQHANDLER_H_ + +#include "inputEventHandler.h" + +class MagicSysRqHandler : public DefaultInputEventHandler +{ +public: + void handle(InputEvent const& evt, InputEventContext const* ctx); + + static void describeInto(QList& list) + { + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Reboot immediately"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'b'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Crash system"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'c'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show all held logs"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'd'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGTERM to all"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'e'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Activate OOM killer"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'f'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGKILL to all"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'i'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Force thaw filesystems"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'j'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Kill all on terminal"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'k'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show stack traces"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'l'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump memory info"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'm'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Make real-time tasks niceable"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'n'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Power off immediately"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'o'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump registers and flags"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'p'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump timers and clockevents"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'q'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Turn off raw keyboard mode"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'r'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Sync all mounted filesystems"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 's'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump task list"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 't'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Remount all read-only"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'u'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump uninterruptible tasks"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'w'); + list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump ftrace buffer"), + InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'z'); + } +}; + +#endif /* MAGICSYSRQHANDLER_H_ */ -- cgit v1.2.3-55-g7522