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') 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