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/pvsPrivInputSocket.cpp | 196 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/input/pvsPrivInputSocket.cpp (limited to 'src/input/pvsPrivInputSocket.cpp') 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; +} -- cgit v1.2.3-55-g7522 From d783dd243478921031d0f7bca80840429a8b9996 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 14:55:04 +0200 Subject: Change location of pvsprivinputd.conf to /etc/ Also centralize the knowledge of that location so that it lives in one place only. --- src/input/pvsPrivInputSocket.cpp | 31 +++++++++++++++++++++++++++++-- src/input/pvsPrivInputSocket.h | 5 +++++ 2 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src/input/pvsPrivInputSocket.cpp') diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp index 2428582..c491dd9 100644 --- a/src/input/pvsPrivInputSocket.cpp +++ b/src/input/pvsPrivInputSocket.cpp @@ -29,10 +29,37 @@ using namespace std; # define UNIX_PATH_MAX 108 /* according to unix(7) */ #endif +static QSettings* pvsPrivInputSettings = 0; + +QString pvsPrivInputGetSettingsPath() +{ + return "/etc/pvsprivinputd.conf"; +} + +QSettings* pvsPrivInputGetSettings() +{ + if(!pvsPrivInputSettings) + { + pvsPrivInputSettings = new QSettings(pvsPrivInputGetSettingsPath(), QSettings::IniFormat); + } + return pvsPrivInputSettings; +} + +QSettings* pvsPrivInputReopenSettings() +{ + if(pvsPrivInputSettings) + { + delete pvsPrivInputSettings; + pvsPrivInputSettings = 0; + } + return pvsPrivInputGetSettings(); +} + QString pvsPrivInputGetSocketAddress() { - QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsprivinputd"); - return settings.value("socketpath", "/tmp/pvsprivinputd.sock").toString(); + return pvsPrivInputGetSettings()->value("socketpath", "/tmp/pvsprivinputd.sock").toString(); +} + } int pvsPrivInputMakeClientSocket() diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h index e6fb0c0..879ca99 100644 --- a/src/input/pvsPrivInputSocket.h +++ b/src/input/pvsPrivInputSocket.h @@ -22,6 +22,11 @@ #include #include +class QSettings; + +QSettings* pvsPrivInputGetSettings(); +QSettings* pvsPrivInputReopenSettings(); +QString pvsPrivInputGetSettingsPath(); QString pvsPrivInputGetSocketAddress(); int pvsPrivInputMakeClientSocket(); int pvsPrivInputMakeServerSocket(); -- 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/pvsPrivInputSocket.cpp') 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