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 + src/input/inputEvent.cpp | 98 +++++ src/input/inputEvent.h | 218 ++++++++++ src/input/inputEventHandler.h | 187 ++++++++ src/input/inputEventNonQt.cpp | 17 + src/input/unprivilegedHandlerChain.h | 34 ++ src/input/x11FakeKeyboardHandler.cpp | 800 +++++++++++++++++++++++++++++++++++ src/input/x11FakeKeyboardHandler.h | 29 ++ src/input/x11FakeMouseHandler.cpp | 59 +++ src/input/x11FakeMouseHandler.h | 34 ++ src/input/x11InputUtils.cpp | 29 ++ src/input/x11InputUtils.h | 27 ++ 12 files changed, 1551 insertions(+) create mode 100644 src/input/CMakeLists.txt create mode 100644 src/input/inputEvent.cpp create mode 100644 src/input/inputEvent.h create mode 100644 src/input/inputEventHandler.h create mode 100644 src/input/inputEventNonQt.cpp create mode 100644 src/input/unprivilegedHandlerChain.h create mode 100644 src/input/x11FakeKeyboardHandler.cpp create mode 100644 src/input/x11FakeKeyboardHandler.h create mode 100644 src/input/x11FakeMouseHandler.cpp create mode 100644 src/input/x11FakeMouseHandler.h create mode 100644 src/input/x11InputUtils.cpp create mode 100644 src/input/x11InputUtils.h (limited to 'src/input') 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} +) diff --git a/src/input/inputEvent.cpp b/src/input/inputEvent.cpp new file mode 100644 index 0000000..04a84e7 --- /dev/null +++ b/src/input/inputEvent.cpp @@ -0,0 +1,98 @@ +/* + * inputEvent.cpp + * + * Created on: 06.09.2010 + * Author: brs + */ + +#include +#include +#include +#include +#include +#include +#include "inputEvent.h" +#include + +// We implement operators to serialize and load an event: +QDataStream& operator <<(QDataStream& ostrm, InputEvent const& evt) +{ + ostrm << evt.type_ << evt.code_ << evt.value_; + return ostrm; +} + +QDataStream& operator >>(QDataStream& istrm, InputEvent& evt) +{ + istrm >> evt.type_ >> evt.code_ >> evt.value_; + return istrm; +} + +void eventToString(InputEvent const& evt, QString& str) +{ + QByteArray ba; + QBuffer buf(&ba); + buf.open(QIODevice::WriteOnly); + QDataStream out(&buf); + out << evt; + str = QString::fromAscii(ba.toBase64()); +} + +bool eventFromString(QString const& str, InputEvent& evt) +{ + // TODO This does not do proper error checking. Only use from trusted sources! + QByteArray ba = QByteArray::fromBase64(str.toAscii()); + QBuffer buf(&ba); + buf.open(QIODevice::ReadOnly); + QDataStream in(&buf); + in >> evt; + return true; +} + +quint16 InputEvent::mouseButtonsFromQt(int b) +{ + quint16 ret = 0; + if(b & Qt::LeftButton) + { + ret |= EB_LEFT; + } + if(b & Qt::RightButton) + { + ret |= EB_RIGHT; + } + if(b & Qt::MidButton) + { + ret |= EB_MIDDLE; + } + return ret; +} + +InputEvent InputEvent::mousePressRelease(QMouseEvent const* evt) +{ + quint16 button = mouseButtonsFromQt(evt->button()); + quint16 buttons = mouseButtonsFromQt(evt->buttons()); + quint16 code; + + if(buttons & button) + { + code = EC_PRESS; + } + else + { + code = EC_RELEASE; + } + + quint32 value = ((quint32)button << 16) | buttons; + return InputEvent(ET_BUTTON, code, value); +} + +InputEvent InputEvent::keyboardPress(QKeyEvent const* evt) +{ + quint32 value = evt->key() | evt->modifiers(); + return InputEvent(ET_KEY, EC_PRESS, value); +} + +InputEvent InputEvent::keyboardRelease(QKeyEvent const* evt) +{ + quint32 value = evt->key() | evt->modifiers(); + return InputEvent(ET_KEY, EC_RELEASE, value); +} diff --git a/src/input/inputEvent.h b/src/input/inputEvent.h new file mode 100644 index 0000000..917cc64 --- /dev/null +++ b/src/input/inputEvent.h @@ -0,0 +1,218 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # inputEvent.h: + # - Definition of an input event + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTEVENT_H_ +#define INPUTEVENT_H_ + +#include +#include +#include +#include + +#ifndef __linux +# error "This will only run on a Linux system. Porting is required for other systems." +#endif + +struct QDataStream; +struct QString; +struct QMouseEvent; +struct QKeyEvent; + +class InputEvent +{ +private: + friend QDataStream& operator<<(QDataStream&, InputEvent const&); + friend QDataStream& operator>>(QDataStream&, InputEvent&); + + friend void eventToString(InputEvent const& evt, QString& str); + friend bool eventFromString(QString const& str, InputEvent& evt); + + uint16_t type_; + uint16_t code_; + uint32_t value_; + + // InputEvents are immutable. Prohibit assignment: + InputEvent& operator=(InputEvent const&); // There intentionally is no implementation. +public: + InputEvent(uint16_t type, uint16_t code, uint32_t value) : type_(type), code_(code), value_(value) + { + } + + InputEvent() + { + } + + static InputEvent mouseMotion(uint16_t x, uint16_t y) + { + return InputEvent(ET_POINTER, 0, ((uint32_t)x << 16) | y); + } + + static uint16_t mouseButtonsFromQt(int b); + + static InputEvent mousePressRelease(QMouseEvent const* event); + static InputEvent keyboardPress(QKeyEvent const* event); + static InputEvent keyboardRelease(QKeyEvent const* event); + + static const uint16_t ET_KEY = 0; + static const uint16_t ET_BUTTON = 1; + static const uint16_t ET_POINTER = 2; + static const uint16_t ET_SPECIAL = 3; + + static const uint16_t EC_PRESS = 0; + static const uint16_t EC_RELEASE = 1; + static const uint16_t EC_REBOOT = 2; + static const uint16_t EC_SYSRQ = 3; + static const uint16_t EC_KILL_X = 4; + + typedef uint32_t event_key; + + typedef uint32_t event_key_modifiers; + + static const uint16_t EB_LEFT = 1; + static const uint16_t EB_MIDDLE = 2; + static const uint16_t EB_RIGHT = 4; + + static const uint32_t MODIFIER_MASK = + 0x7e000000; + + uint16_t type() const + { + return type_; + } + + uint16_t code() const + { + return code_; + } + + uint32_t value() const + { + return value_; + } + + bool isKeyboard() const + { + return type_ == ET_KEY; + } + + bool isButton() const + { + return type_ == ET_BUTTON; + } + + bool isPointer() const + { + return type_ == ET_POINTER; + } + + bool isSpecial() const + { + return type_ == ET_SPECIAL; + } + + bool isPress() const + { + return code_ == EC_PRESS; + } + + bool isRelease() const + { + return code_ == EC_RELEASE; + } + + uint16_t pressedButton() const + { + assert(type_ == ET_BUTTON); + return (value_ >> 16); + } + + uint16_t heldButtons() const + { + assert(type_ == ET_BUTTON); + return (value_ & 0xffff); + } + + uint16_t xCoord() const + { + assert(type_ == ET_POINTER); + return (value_ >> 16); + } + + uint16_t yCoord() const + { + assert(type_ == ET_POINTER); + return (value_ & 0xffff); + } + + static std::string typeToString(uint16_t type) + { + switch(type) + { + case ET_BUTTON: + return "BUTTON"; + case ET_KEY: + return "KEY"; + case ET_POINTER: + return "POINTER"; + case ET_SPECIAL: + return "SPECIAL"; + default: + std::ostringstream s; + s << std::hex << type; + return s.str(); + } + } + + static std::string codeToString(uint16_t code) + { + switch(code) + { + case EC_PRESS: + return "PRESS"; + case EC_RELEASE: + return "RELEASE"; + case EC_SYSRQ: + return "SYSRQ"; + case EC_REBOOT: + return "REBOOT"; + case EC_KILL_X: + return "KILL_X"; + default: + std::ostringstream s; + s << std::hex << code; + return s.str(); + } + } + + std::string toString() const + { + std::ostringstream s; + s << typeToString(type_) << ':' << codeToString(code_) << ':' << std::hex << value_; + return s.str(); + } + + uint32_t qt_keysym() const + { + return value_ & ~MODIFIER_MASK; + } + + uint32_t qt_modifiers() const + { + return value_ & MODIFIER_MASK; + } +}; + +#endif /* INPUTEVENT_H_ */ diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h new file mode 100644 index 0000000..3910f93 --- /dev/null +++ b/src/input/inputEventHandler.h @@ -0,0 +1,187 @@ +/* + # 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 + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTEVENTHANDLER_H_ +#define INPUTEVENTHANDLER_H_ + +#include +#include +#include +#include +#include + +#define HANDLER_TYPE_DONT_CARE 0xffff +#define HANDLER_CODE_DONT_CARE 0xffff +#define HANDLER_VALUE_DONT_CARE 0xffffffff + +template +class DefaultInputEventHandler { +public: + static bool matches(InputEvent const& evt) { + if(Type != 0xffff) { + if(evt.type() != Type) + return false; + } + if(Code != 0xffff) { + if(evt.code() != Code) + return false; + } + if(Value != 0xffffffff) { + if(evt.value() != Value) + return false; + } + return true; + } + + static void initialize() + { + } +}; + +namespace policy { + +struct NoSecurityCheck { + static bool allow(InputEvent const&) { + return true; + } +}; + +struct PhysicalSeatSecurityCheck { + static bool allow(InputEvent const&) { + return /* TODO implement */ true; + } +}; + +struct AlwaysDenySecurityCheck { + static bool allow(InputEvent const&) { + return false; + } +}; + +struct UnixLike; +struct Linux; +struct Windows; + +#if defined(__linux) +typedef boost::mpl::vector2::type Systems; +#elif defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) +typedef boost::mpl::vector1::type Systems; +#else +# error "Porting is needed!" +#endif + +struct SystemEnabled; +struct SystemDisabled; + +template +struct RequireSystem +{ + typedef typename boost::mpl::contains::type enabled_type; + static const bool enabled = enabled_type::value; +}; + +struct RequireNoSystem +{ + typedef boost::mpl::bool_::type enabled_type; + static const bool enabled = enabled_type::value; +}; + +} + +template +class HandlerHelper +{ +public: + static bool handle(InputEvent const& evt) { + if(!SecurityPolicy::allow(evt)) + { + return true; + } + if(Delegate::matches(evt)) { + Delegate::handle(evt); + return true; + } else { + return false; + } + } + + static void initialize() + { + Delegate::initialize(); + } +}; + +template +class HandlerHelper +{ +public: + static bool handle(InputEvent const& evt) { + return false; + } + + static void initialize() + { + } +}; + +template +struct Handler : public HandlerHelper +{ +}; + +template +struct InputEventHandlerChainHelper +{ +private: + typedef typename boost::mpl::next::type next_iterator_type; + typedef InputEventHandlerChainHelper next_in_chain; + + typedef typename boost::mpl::deref::type handler_type; + +public: + static void handle(InputEvent const& evt) { + if(!handler_type::handle(evt)) { + next_in_chain::handle(evt); + } + } + + static void initialize() { + handler_type::initialize(); + next_in_chain::initialize(); + } +}; + +template +struct InputEventHandlerChainHelper +{ +public: + static void handle(InputEvent const&) { + // do nothing + } + + static void initialize() { + // do nothing + } +}; + +template +struct InputEventHandlerChain : public InputEventHandlerChainHelper::type, typename boost::mpl::end::type> +{ +}; + +#endif /* INPUTEVENTHANDLER_H_ */ diff --git a/src/input/inputEventNonQt.cpp b/src/input/inputEventNonQt.cpp new file mode 100644 index 0000000..a7455fd --- /dev/null +++ b/src/input/inputEventNonQt.cpp @@ -0,0 +1,17 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # inputEvent.h: + # - Input Event routines that need not be linked against Qt + # -------------------------------------------------------------------------- + */ + + diff --git a/src/input/unprivilegedHandlerChain.h b/src/input/unprivilegedHandlerChain.h new file mode 100644 index 0000000..734720a --- /dev/null +++ b/src/input/unprivilegedHandlerChain.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/ + # -------------------------------------------------------------------------- + # inputHandlerChain.h: + # - Definition of the input handler chain + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTHANDLERCHAIN_H_ +#define INPUTHANDLERCHAIN_H_ + +#include +#include "inputEventHandler.h" + +#include "x11FakeKeyboardHandler.h" +#include "x11FakeMouseHandler.h" + +typedef boost::mpl::list< + Handler >, + Handler >, + Handler > +>::type unprivileged_handler_list; + +typedef InputEventHandlerChain unprivileged_handler_chain; + +#endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp new file mode 100644 index 0000000..bb14b76 --- /dev/null +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -0,0 +1,800 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # x11FakeKeyboardHandler.h: + # - Handle keyboard events on X11 - interface + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +// Qt headers need to be included before X11 headers +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include "x11InputUtils.h" +#include "x11FakeKeyboardHandler.h" + +//////////////////////// INPUT EVENT TRANSLATION ///////////////////////////////// + +typedef unsigned char xmodifier_type; + +char modifiernames[][8] = { + "SHIFT", + "LOCK", + "CONTROL", + "MOD1", + "MOD2", + "MOD3", + "MOD4", + "MOD5" +}; + +QString modifiers_to_string(xmodifier_type mods) +{ + QString s; + for(int i = 0; i < 8; i++) + { + if(mods & (1< lookup_table_type; +typedef lookup_table_type::const_iterator lookup_table_const_iterator; +typedef lookup_table_type::iterator lookup_table_iterator; +lookup_table_type keysyms; +void initialize_keysyms() { + keysyms[Qt::Key_Escape] = XK_Escape; + keysyms[Qt::Key_Tab] = XK_Tab; +// keysyms[Qt::Key_Backtab] = XK_Backtab; + keysyms[Qt::Key_Backspace] = XK_BackSpace; + keysyms[Qt::Key_Return] = XK_Return; + keysyms[Qt::Key_Enter] = XK_KP_Enter; + keysyms[Qt::Key_Insert] = XK_Insert; + keysyms[Qt::Key_Delete] = XK_Delete; + keysyms[Qt::Key_Pause] = XK_Pause; + keysyms[Qt::Key_Print] = XK_Print; + keysyms[Qt::Key_SysReq] = XK_Sys_Req; + keysyms[Qt::Key_Clear] = XK_Clear; + keysyms[Qt::Key_Home] = XK_Home; + keysyms[Qt::Key_End] = XK_End; + keysyms[Qt::Key_Left] = XK_Left; + keysyms[Qt::Key_Up] = XK_Up; + keysyms[Qt::Key_Right] = XK_Right; + keysyms[Qt::Key_Down] = XK_Down; + keysyms[Qt::Key_PageUp] = XK_Page_Up; + keysyms[Qt::Key_PageDown] = XK_Page_Down; + keysyms[Qt::Key_Shift] = XK_Shift_L; + keysyms[Qt::Key_Control] = XK_Control_L; + keysyms[Qt::Key_Meta] = XK_Meta_L; + keysyms[Qt::Key_Alt] = XK_Alt_L; + keysyms[Qt::Key_CapsLock] = XK_Caps_Lock; + keysyms[Qt::Key_NumLock] = XK_Num_Lock; + keysyms[Qt::Key_ScrollLock] = XK_Scroll_Lock; + keysyms[Qt::Key_F1] = XK_F1; + keysyms[Qt::Key_F2] = XK_F2; + keysyms[Qt::Key_F3] = XK_F3; + keysyms[Qt::Key_F4] = XK_F4; + keysyms[Qt::Key_F5] = XK_F5; + keysyms[Qt::Key_F6] = XK_F6; + keysyms[Qt::Key_F7] = XK_F7; + keysyms[Qt::Key_F8] = XK_F8; + keysyms[Qt::Key_F9] = XK_F9; + keysyms[Qt::Key_F10] = XK_F10; + keysyms[Qt::Key_F11] = XK_F11; + keysyms[Qt::Key_F12] = XK_F12; + keysyms[Qt::Key_F13] = XK_F13; + keysyms[Qt::Key_F14] = XK_F14; + keysyms[Qt::Key_F15] = XK_F15; + keysyms[Qt::Key_F16] = XK_F16; + keysyms[Qt::Key_F17] = XK_F17; + keysyms[Qt::Key_F18] = XK_F18; + keysyms[Qt::Key_F19] = XK_F19; + keysyms[Qt::Key_F20] = XK_F20; + keysyms[Qt::Key_F21] = XK_F21; + keysyms[Qt::Key_F22] = XK_F22; + keysyms[Qt::Key_F23] = XK_F23; + keysyms[Qt::Key_F24] = XK_F24; + keysyms[Qt::Key_F25] = XK_F25; + keysyms[Qt::Key_F26] = XK_F26; + keysyms[Qt::Key_F27] = XK_F27; + keysyms[Qt::Key_F28] = XK_F28; + keysyms[Qt::Key_F29] = XK_F29; + keysyms[Qt::Key_F30] = XK_F30; + keysyms[Qt::Key_F31] = XK_F31; + keysyms[Qt::Key_F32] = XK_F32; + keysyms[Qt::Key_F33] = XK_F33; + keysyms[Qt::Key_F34] = XK_F34; + keysyms[Qt::Key_F35] = XK_F35; + keysyms[Qt::Key_Super_L] = XK_Super_L; + keysyms[Qt::Key_Super_R] = XK_Super_R; + keysyms[Qt::Key_Menu] = XK_Menu; + keysyms[Qt::Key_Hyper_L] = XK_Hyper_L; + keysyms[Qt::Key_Hyper_R] = XK_Hyper_R; + keysyms[Qt::Key_Help] = XK_Help; +// keysyms[Qt::Key_Direction_L] = XK_Direction_L; +// keysyms[Qt::Key_Direction_R] = XK_Direction_R; + keysyms[Qt::Key_Space] = XK_space; + keysyms[Qt::Key_Exclam] = XK_exclam; + keysyms[Qt::Key_QuoteDbl] = XK_quotedbl; + keysyms[Qt::Key_NumberSign] = XK_numbersign; + keysyms[Qt::Key_Dollar] = XK_dollar; + keysyms[Qt::Key_Percent] = XK_percent; + keysyms[Qt::Key_Ampersand] = XK_ampersand; + keysyms[Qt::Key_Apostrophe] = XK_apostrophe; + keysyms[Qt::Key_ParenLeft] = XK_parenleft; + keysyms[Qt::Key_ParenRight] = XK_parenright; + keysyms[Qt::Key_Asterisk] = XK_asterisk; + keysyms[Qt::Key_Plus] = XK_plus; + keysyms[Qt::Key_Comma] = XK_comma; + keysyms[Qt::Key_Minus] = XK_minus; + keysyms[Qt::Key_Period] = XK_period; + keysyms[Qt::Key_Slash] = XK_slash; + keysyms[Qt::Key_0] = XK_0; + keysyms[Qt::Key_1] = XK_1; + keysyms[Qt::Key_2] = XK_2; + keysyms[Qt::Key_3] = XK_3; + keysyms[Qt::Key_4] = XK_4; + keysyms[Qt::Key_5] = XK_5; + keysyms[Qt::Key_6] = XK_6; + keysyms[Qt::Key_7] = XK_7; + keysyms[Qt::Key_8] = XK_8; + keysyms[Qt::Key_9] = XK_9; + keysyms[Qt::Key_Colon] = XK_colon; + keysyms[Qt::Key_Semicolon] = XK_semicolon; + keysyms[Qt::Key_Less] = XK_less; + keysyms[Qt::Key_Equal] = XK_equal; + keysyms[Qt::Key_Greater] = XK_greater; + keysyms[Qt::Key_Question] = XK_question; + keysyms[Qt::Key_At] = XK_at; + keysyms[Qt::Key_A] = XK_A; + keysyms[Qt::Key_B] = XK_B; + keysyms[Qt::Key_C] = XK_C; + keysyms[Qt::Key_D] = XK_D; + keysyms[Qt::Key_E] = XK_E; + keysyms[Qt::Key_F] = XK_F; + keysyms[Qt::Key_G] = XK_G; + keysyms[Qt::Key_H] = XK_H; + keysyms[Qt::Key_I] = XK_I; + keysyms[Qt::Key_J] = XK_J; + keysyms[Qt::Key_K] = XK_K; + keysyms[Qt::Key_L] = XK_L; + keysyms[Qt::Key_M] = XK_M; + keysyms[Qt::Key_N] = XK_N; + keysyms[Qt::Key_O] = XK_O; + keysyms[Qt::Key_P] = XK_P; + keysyms[Qt::Key_Q] = XK_Q; + keysyms[Qt::Key_R] = XK_R; + keysyms[Qt::Key_S] = XK_S; + keysyms[Qt::Key_T] = XK_T; + keysyms[Qt::Key_U] = XK_U; + keysyms[Qt::Key_V] = XK_V; + keysyms[Qt::Key_W] = XK_W; + keysyms[Qt::Key_X] = XK_X; + keysyms[Qt::Key_Y] = XK_Y; + keysyms[Qt::Key_Z] = XK_Z; + keysyms[Qt::Key_BracketLeft] = XK_bracketleft; + keysyms[Qt::Key_Backslash] = XK_backslash; + keysyms[Qt::Key_BracketRight] = XK_bracketright; + keysyms[Qt::Key_AsciiCircum] = XK_asciicircum; + keysyms[Qt::Key_Underscore] = XK_underscore; + keysyms[Qt::Key_QuoteLeft] = XK_quoteleft; + keysyms[Qt::Key_BraceLeft] = XK_braceleft; + keysyms[Qt::Key_Bar] = XK_bar; + keysyms[Qt::Key_BraceRight] = XK_braceright; + keysyms[Qt::Key_AsciiTilde] = XK_asciitilde; + keysyms[Qt::Key_nobreakspace] = XK_nobreakspace; + keysyms[Qt::Key_exclamdown] = XK_exclamdown; + keysyms[Qt::Key_cent] = XK_cent; + keysyms[Qt::Key_sterling] = XK_sterling; + keysyms[Qt::Key_currency] = XK_currency; + keysyms[Qt::Key_yen] = XK_yen; + keysyms[Qt::Key_brokenbar] = XK_brokenbar; + keysyms[Qt::Key_section] = XK_section; + keysyms[Qt::Key_diaeresis] = XK_diaeresis; + keysyms[Qt::Key_copyright] = XK_copyright; + keysyms[Qt::Key_ordfeminine] = XK_ordfeminine; + keysyms[Qt::Key_guillemotleft] = XK_guillemotleft; + keysyms[Qt::Key_notsign] = XK_notsign; + keysyms[Qt::Key_hyphen] = XK_hyphen; + keysyms[Qt::Key_registered] = XK_registered; + keysyms[Qt::Key_macron] = XK_macron; + keysyms[Qt::Key_degree] = XK_degree; + keysyms[Qt::Key_plusminus] = XK_plusminus; + keysyms[Qt::Key_twosuperior] = XK_twosuperior; + keysyms[Qt::Key_threesuperior] = XK_threesuperior; + keysyms[Qt::Key_acute] = XK_acute; + keysyms[Qt::Key_mu] = XK_mu; + keysyms[Qt::Key_paragraph] = XK_paragraph; + keysyms[Qt::Key_periodcentered] = XK_periodcentered; + keysyms[Qt::Key_cedilla] = XK_cedilla; + keysyms[Qt::Key_onesuperior] = XK_onesuperior; + keysyms[Qt::Key_masculine] = XK_masculine; + keysyms[Qt::Key_guillemotright] = XK_guillemotright; + keysyms[Qt::Key_onequarter] = XK_onequarter; + keysyms[Qt::Key_onehalf] = XK_onehalf; + keysyms[Qt::Key_threequarters] = XK_threequarters; + keysyms[Qt::Key_questiondown] = XK_questiondown; + keysyms[Qt::Key_Agrave] = XK_Agrave; + keysyms[Qt::Key_Aacute] = XK_Aacute; + keysyms[Qt::Key_Acircumflex] = XK_Acircumflex; + keysyms[Qt::Key_Atilde] = XK_Atilde; + keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis; + keysyms[Qt::Key_Aring] = XK_Aring; + keysyms[Qt::Key_AE] = XK_AE; + keysyms[Qt::Key_Ccedilla] = XK_Ccedilla; + keysyms[Qt::Key_Egrave] = XK_Egrave; + keysyms[Qt::Key_Eacute] = XK_Eacute; + keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex; + keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis; + keysyms[Qt::Key_Igrave] = XK_Igrave; + keysyms[Qt::Key_Iacute] = XK_Iacute; + keysyms[Qt::Key_Icircumflex] = XK_Icircumflex; + keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis; + keysyms[Qt::Key_ETH] = XK_ETH; + keysyms[Qt::Key_Ntilde] = XK_Ntilde; + keysyms[Qt::Key_Ograve] = XK_Ograve; + keysyms[Qt::Key_Oacute] = XK_Oacute; + keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex; + keysyms[Qt::Key_Otilde] = XK_Otilde; + keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis; + keysyms[Qt::Key_multiply] = XK_multiply; + keysyms[Qt::Key_Ooblique] = XK_Ooblique; + keysyms[Qt::Key_Ugrave] = XK_Ugrave; + keysyms[Qt::Key_Uacute] = XK_Uacute; + keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex; + keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis; + keysyms[Qt::Key_Yacute] = XK_Yacute; + keysyms[Qt::Key_THORN] = XK_THORN; + keysyms[Qt::Key_ssharp] = XK_ssharp; + keysyms[Qt::Key_Agrave] = XK_Agrave; + keysyms[Qt::Key_Aacute] = XK_Aacute; + keysyms[Qt::Key_Acircumflex] = XK_Acircumflex; + keysyms[Qt::Key_Atilde] = XK_Atilde; + keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis; + keysyms[Qt::Key_Aring] = XK_Aring; + keysyms[Qt::Key_AE] = XK_AE; + keysyms[Qt::Key_Ccedilla] = XK_Ccedilla; + keysyms[Qt::Key_Egrave] = XK_Egrave; + keysyms[Qt::Key_Eacute] = XK_Eacute; + keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex; + keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis; + keysyms[Qt::Key_Igrave] = XK_Igrave; + keysyms[Qt::Key_Iacute] = XK_Iacute; + keysyms[Qt::Key_Icircumflex] = XK_Icircumflex; + keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis; + keysyms[Qt::Key_ETH] = XK_ETH; + keysyms[Qt::Key_Ntilde] = XK_Ntilde; + keysyms[Qt::Key_Ograve] = XK_Ograve; + keysyms[Qt::Key_Oacute] = XK_Oacute; + keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex; + keysyms[Qt::Key_Otilde] = XK_Otilde; + keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis; + keysyms[Qt::Key_division] = XK_division; + keysyms[Qt::Key_Ooblique] = XK_Ooblique; + keysyms[Qt::Key_Ugrave] = XK_Ugrave; + keysyms[Qt::Key_Uacute] = XK_Uacute; + keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex; + keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis; + keysyms[Qt::Key_Yacute] = XK_Yacute; + keysyms[Qt::Key_THORN] = XK_THORN; + keysyms[Qt::Key_ydiaeresis] = XK_ydiaeresis; + keysyms[Qt::Key_AltGr] = XK_ISO_Level3_Shift; + keysyms[Qt::Key_Multi_key] = XK_Multi_key; + keysyms[Qt::Key_Codeinput] = XK_Codeinput; + keysyms[Qt::Key_SingleCandidate] = XK_SingleCandidate; + keysyms[Qt::Key_MultipleCandidate] = XK_MultipleCandidate; + keysyms[Qt::Key_PreviousCandidate] = XK_PreviousCandidate; + keysyms[Qt::Key_Mode_switch] = XK_Mode_switch; +// keysyms[Qt::Key_script_switch] = XK_script_switch; + keysyms[Qt::Key_Kanji] = XK_Kanji; + keysyms[Qt::Key_Muhenkan] = XK_Muhenkan; +// keysyms[Qt::Key_Henkan_Mode] = XK_Henkan_Mode; + keysyms[Qt::Key_Henkan] = XK_Henkan; + keysyms[Qt::Key_Romaji] = XK_Romaji; + keysyms[Qt::Key_Hiragana] = XK_Hiragana; + keysyms[Qt::Key_Katakana] = XK_Katakana; + keysyms[Qt::Key_Hiragana_Katakana] = XK_Hiragana_Katakana; + keysyms[Qt::Key_Zenkaku] = XK_Zenkaku; + keysyms[Qt::Key_Hankaku] = XK_Hankaku; + keysyms[Qt::Key_Zenkaku_Hankaku] = XK_Zenkaku_Hankaku; + keysyms[Qt::Key_Touroku] = XK_Touroku; + keysyms[Qt::Key_Massyo] = XK_Massyo; + keysyms[Qt::Key_Kana_Lock] = XK_Kana_Lock; + keysyms[Qt::Key_Kana_Shift] = XK_Kana_Shift; + keysyms[Qt::Key_Eisu_Shift] = XK_Eisu_Shift; + keysyms[Qt::Key_Eisu_toggle] = XK_Eisu_toggle; +// keysyms[Qt::Key_Kanji_Bangou] = XK_Kanji_Bangou; +// keysyms[Qt::Key_Zen_Koho] = XK_Zen_Koho; +// keysyms[Qt::Key_Mae_Koho] = XK_Mae_Koho; + keysyms[Qt::Key_Hangul] = XK_Hangul; + keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja; + keysyms[Qt::Key_Hangul] = XK_Hangul; + keysyms[Qt::Key_Hangul_Start] = XK_Hangul_Start; + keysyms[Qt::Key_Hangul_End] = XK_Hangul_End; + keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja; + keysyms[Qt::Key_Hangul_Jamo] = XK_Hangul_Jamo; + keysyms[Qt::Key_Hangul_Romaja] = XK_Hangul_Romaja; +// keysyms[Qt::Key_Hangul_Codeinput] = XK_Hangul_Codeinput; + keysyms[Qt::Key_Hangul_Jeonja] = XK_Hangul_Jeonja; + keysyms[Qt::Key_Hangul_Banja] = XK_Hangul_Banja; + keysyms[Qt::Key_Hangul_PreHanja] = XK_Hangul_PreHanja; + keysyms[Qt::Key_Hangul_PostHanja] = XK_Hangul_PostHanja; +// keysyms[Qt::Key_Hangul_SingleCandidate] = XK_Hangul_SingleCandidate; +// keysyms[Qt::Key_Hangul_MultipleCandidate] = XK_Hangul_MultipleCandidate; +// keysyms[Qt::Key_Hangul_PreviousCandidate] = XK_Hangul_PreviousCandidate; + keysyms[Qt::Key_Hangul_Special] = XK_Hangul_Special; +// keysyms[Qt::Key_Hangul_switch] = XK_Hangul_switch; + keysyms[Qt::Key_Dead_Grave] = XK_dead_grave; + keysyms[Qt::Key_Dead_Acute] = XK_dead_acute; + keysyms[Qt::Key_Dead_Circumflex] = XK_dead_circumflex; + keysyms[Qt::Key_Dead_Tilde] = XK_dead_tilde; + keysyms[Qt::Key_Dead_Macron] = XK_dead_macron; + keysyms[Qt::Key_Dead_Breve] = XK_dead_breve; + keysyms[Qt::Key_Dead_Abovedot] = XK_dead_abovedot; + keysyms[Qt::Key_Dead_Diaeresis] = XK_dead_diaeresis; + keysyms[Qt::Key_Dead_Abovering] = XK_dead_abovering; + keysyms[Qt::Key_Dead_Doubleacute] = XK_dead_doubleacute; + keysyms[Qt::Key_Dead_Caron] = XK_dead_caron; + keysyms[Qt::Key_Dead_Cedilla] = XK_dead_cedilla; + keysyms[Qt::Key_Dead_Ogonek] = XK_dead_ogonek; + keysyms[Qt::Key_Dead_Iota] = XK_dead_iota; + keysyms[Qt::Key_Dead_Voiced_Sound] = XK_dead_voiced_sound; + keysyms[Qt::Key_Dead_Semivoiced_Sound] = XK_dead_semivoiced_sound; + keysyms[Qt::Key_Dead_Belowdot] = XK_dead_belowdot; + keysyms[Qt::Key_Dead_Hook] = XK_dead_hook; + keysyms[Qt::Key_Dead_Horn] = XK_dead_horn; +// keysyms[Qt::Key_Back] = XK_Back; +// keysyms[Qt::Key_Forward] = XK_Forward; +// keysyms[Qt::Key_Stop] = XK_Stop; +// keysyms[Qt::Key_Refresh] = XK_Refresh; +// keysyms[Qt::Key_VolumeDown] = XK_VolumeDown; +// keysyms[Qt::Key_VolumeMute] = XK_VolumeMute; +// keysyms[Qt::Key_VolumeUp] = XK_VolumeUp; +// keysyms[Qt::Key_BassBoost] = XK_BassBoost; +// keysyms[Qt::Key_BassUp] = XK_BassUp; +// keysyms[Qt::Key_BassDown] = XK_BassDown; +// keysyms[Qt::Key_TrebleUp] = XK_TrebleUp; +// keysyms[Qt::Key_TrebleDown] = XK_TrebleDown; +// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay; +// keysyms[Qt::Key_MediaStop] = XK_MediaStop; +// keysyms[Qt::Key_MediaPrevious] = XK_MediaPrevious; +// keysyms[Qt::Key_MediaNext] = XK_MediaNext; +// keysyms[Qt::Key_MediaRecord] = XK_MediaRecord; +// keysyms[Qt::Key_HomePage] = XK_HomePage; +// keysyms[Qt::Key_Favorites] = XK_Favorites; +// keysyms[Qt::Key_Search] = XK_Search; +// keysyms[Qt::Key_Standby] = XK_Standby; +// keysyms[Qt::Key_OpenUrl] = XK_OpenUrl; +// keysyms[Qt::Key_LaunchMail] = XK_LaunchMail; +// keysyms[Qt::Key_LaunchMedia] = XK_LaunchMedia; +// keysyms[Qt::Key_Launch0] = XK_Launch0; +// keysyms[Qt::Key_Launch1] = XK_Launch1; +// keysyms[Qt::Key_Launch2] = XK_Launch2; +// keysyms[Qt::Key_Launch3] = XK_Launch3; +// keysyms[Qt::Key_Launch4] = XK_Launch4; +// keysyms[Qt::Key_Launch5] = XK_Launch5; +// keysyms[Qt::Key_Launch6] = XK_Launch6; +// keysyms[Qt::Key_Launch7] = XK_Launch7; +// keysyms[Qt::Key_Launch8] = XK_Launch8; +// keysyms[Qt::Key_Launch9] = XK_Launch9; +// keysyms[Qt::Key_LaunchA] = XK_LaunchA; +// keysyms[Qt::Key_LaunchB] = XK_LaunchB; +// keysyms[Qt::Key_LaunchC] = XK_LaunchC; +// keysyms[Qt::Key_LaunchD] = XK_LaunchD; +// keysyms[Qt::Key_LaunchE] = XK_LaunchE; +// keysyms[Qt::Key_LaunchF] = XK_LaunchF; +// keysyms[Qt::Key_Display] = XK_Display; +// keysyms[Qt::Key_MediaLast] = XK_MediaLast; + keysyms[Qt::Key_Select] = XK_Select; +// keysyms[Qt::Key_Yes] = XK_Yes; +// keysyms[Qt::Key_No] = XK_No; + keysyms[Qt::Key_Cancel] = XK_Cancel; +// keysyms[Qt::Key_Printer] = XK_Printer; + keysyms[Qt::Key_Execute] = XK_Execute; +// keysyms[Qt::Key_Sleep] = XK_Sleep; +// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay; +// keysyms[Qt::Key_Zoom] = XK_Zoom; +// keysyms[Qt::Key_Jisho] = XK_Jisho; +// keysyms[Qt::Key_Oyayubi_Left] = XK_Oyayubi_Left; +// keysyms[Qt::Key_Oyayubi_Right] = XK_Oyayubi_Right; +// keysyms[Qt::Key_Context1] = XK_Context1; +// keysyms[Qt::Key_Context2] = XK_Context2; +// keysyms[Qt::Key_Context3] = XK_Context3; +// keysyms[Qt::Key_Context4] = XK_Context4; +// keysyms[Qt::Key_Call] = XK_Call; +// keysyms[Qt::Key_Hangup] = XK_Hangup; +// keysyms[Qt::Key_Flip] = XK_Flip; + keysyms[Qt::Key_unknown] = XK_VoidSymbol; +} + +/* Store Keycodes for "normal" Latin1 keys: */ +int basic_keycodes[0x100]; +int basic_modifiers[0x100]; + +int modifier_keycodes[8]; +Qt::KeyboardModifier modifier_meaning[8]; + +int xmodifier_from_qtmodifier(Qt::KeyboardModifier m) { + for(int i = 0; i < 8; i++) { + if(m == modifier_meaning[i]) + return i; + } + return -1; +} + +typedef std::map qt_to_xmodifier_type; +typedef std::map x_to_qtmodifier_type; +qt_to_xmodifier_type qt_to_xmodifier; +x_to_qtmodifier_type x_to_qtmodifier; + +typedef std::multimap modifier_to_keycode_map; +modifier_to_keycode_map modifier_to_keycode; +typedef std::map keycode_to_modifier_map; +keycode_to_modifier_map keycode_to_modifier; + +void initialize_basic_keycodes() +{ + for(int i = 0; i < 8; i++) { + modifier_keycodes[i] = -1; + } + for(int i = 0; i < 0x100; i++) { + basic_keycodes[i] = -1; + } + + Display* dpy = X11InputUtils::display(); + int min_keycode, max_keycode; + XDisplayKeycodes(dpy, &min_keycode, &max_keycode); + + int xkb_opcode, xkb_event, xkb_error, xkb_major, xkb_minor; + bool xkb_present = XkbQueryExtension(dpy, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor); + if(xkb_present) { + int keysyms_per_code; + const int count = max_keycode - min_keycode + 1; + KeySym* mapping = XGetKeyboardMapping(dpy, min_keycode, count, &keysyms_per_code); + for(int i = 0; i < count; i++) + { + for(int j = 0; j < keysyms_per_code; j++) + { + const int idx = i * keysyms_per_code + j; + const KeySym ks = mapping[idx]; + const int keycode = min_keycode + i; + + if(ks >= ' ' && ks < 0x100) + { + if(ks == XK_at) { + ConsoleLog writeLine(QString("Keycode %1 (%2) gives `@' with modifiers `%3', but it is really `%4'").arg(keycode).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, 0))).arg(modifiers_to_string(j)).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, j)))); + + } + + if(basic_keycodes[ks] != -1) + continue; // already found + + basic_keycodes[ks] = keycode; + basic_modifiers[ks] = j; + } + } + } + XFree(mapping); + } + else + { + for(int i = min_keycode; i <= max_keycode; i++) + { + for(int j = 0; j <= 0xff; j++) + { + KeySym ks = 0; + unsigned int unconsumed; + if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) + continue; + + if(ks && (ks < 0x100)) + { +// ConsoleLog writeLine(QString("Keycode %1 (%2) seems to give `%3' with modifiers `%4', of which `%5' are unconsumed") +// .arg(i) +// .arg(XKeysymToString(XKeycodeToKeysym(dpy, i, 0))) +// .arg(XKeysymToString(ks)) +// .arg(modifiers_to_string(j)) +// .arg(modifiers_to_string(unconsumed))); + if(basic_keycodes[ks] != -1) + continue; + + basic_keycodes[ks] = i; + basic_modifiers[ks] = unconsumed & j; + } + } + } + } + + // find out which keycodes cause the modifier state bits: + XModifierKeymap* modkm; + modkm = XGetModifierMapping(dpy); + + const int shift_kc = XKeysymToKeycode(dpy, XK_Shift_L); + const int control_kc = XKeysymToKeycode(dpy, XK_Control_L); + const int alt_kc = XKeysymToKeycode(dpy, XK_Alt_L); + const int meta_kc = XKeysymToKeycode(dpy, XK_Meta_L); + const int switch_kc = XKeysymToKeycode(dpy, XK_Mode_switch); + + for(int i = 0; i < 8; i++) { + for(int j = 0; j < modkm->max_keypermod; j++) { + const int idx = i * modkm->max_keypermod + j; + const int kc = modkm->modifiermap[idx]; + + modifier_to_keycode.insert(std::make_pair((1< int_set_type; +int_set_type pressed_modifier_keys; +xmodifier_type current_modifiers; + +void trackModifiers(int keycode, bool down) +{ + // is this a modifier key? + const bool is_modifier = keycode_to_modifier.find(keycode) != keycode_to_modifier.end(); + + if(!is_modifier) + { + return; + } + + if(down) { + pressed_modifier_keys.insert(keycode); + } else { + pressed_modifier_keys.erase(keycode); + } + + int_set_type::iterator i, end = pressed_modifier_keys.end(); + xmodifier_type modifs = 0; + for(i = pressed_modifier_keys.begin(); i != end; i++) + { + keycode_to_modifier_map::iterator found_kc = keycode_to_modifier.find(*i); + if(found_kc != keycode_to_modifier.end()) + { + modifs |= (*found_kc).second; + } + } + current_modifiers = modifs; + + ConsoleLog writeLine(QString("[trackModifiers] current modifiers: %1").arg(modifiers_to_string(modifs))); +} + +typedef std::pair modifier_tweak_type; +typedef std::vector tweak_sequence_type; + +void tweakModifiers(Display* dpy, xmodifier_type neededState, xmodifier_type actualState, tweak_sequence_type& tracker) +{ + ConsoleLog writeLine(QString("tweakModifiers: Trying to get to `%1' from `%2'").arg(modifiers_to_string(neededState)).arg(modifiers_to_string(actualState))); + for(int j = 0; j < 8; j++) + { + xmodifier_type i = 1<second == i) { + kc = *iter; + + // release this key: + ConsoleLog writeLine(QString(" releasing key %1").arg(kc)); + XTestFakeKeyEvent(dpy, kc, 0, CurrentTime); + tracker.push_back(std::make_pair(kc, false)); + } + } + } + + if(kc == -1) { + // strange, but we need to release some other key: + // we don't know which one, so we abort this and hope for the best + continue; + } + } + } +} + +void untweakModifiers(Display* dpy, tweak_sequence_type& tracker) +{ + tweak_sequence_type::iterator i, end = tracker.end(); + for(i = tracker.begin(); i != end; i++) { + modifier_tweak_type& t = *i; + XTestFakeKeyEvent(dpy, t.first, !t.second, CurrentTime); + } +} + +void X11FakeKeyboardHandler::initialize() +{ + initialize_keysyms(); + initialize_basic_keycodes(); + pressed_modifier_keys.clear(); + current_modifiers = 0; +} + +void X11FakeKeyboardHandler::handle(InputEvent const& evt) +{ + Display* dpy = X11InputUtils::display(); + + // find out which keysym caused this event: + lookup_table_const_iterator i = keysyms.find(evt.qt_keysym()); + if(i == keysyms.end()) { + // Special cases. We don't know how to directly translate those, so we will try to emulate them. + switch(evt.qt_keysym()) + { + case Qt::Key_Backtab: + handle(InputEvent(evt.type(), evt.code(), evt.qt_modifiers() | Qt::ShiftModifier | Qt::Key_Tab)); + break; + default: + ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qt_keysym(), 8, 16)); + } + } else { + KeySym ks = (*i).second; + + // is it a "normal" key? + if(ks >= ' ' && ks < 0x100) + { + if(basic_keycodes[ks] != -1) + { + if(evt.isPress()) + { + // Try the simple way first: + // Does the keycode with current modifiers yield the requested symbol? + KeySym unmod_ks = XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers); + ConsoleLog writeLine(QString("Pressing the key for %1 would yield %2 right now.").arg(XKeysymToString(ks)).arg(XKeysymToString(unmod_ks))); + + if(ks == XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers)) + { + XTestFakeKeyEvent(dpy, basic_keycodes[ks], 1, CurrentTime); + } + else + { + // what modifier keys do we need to press? + xmodifier_type mods = translate_modifiers(evt.qt_modifiers()); + + // we may need to press additional modifiers to generate this keysym: + // but, since we do not differentiate upper and lower case, + // and instead let the sender handle those modifiers, + // we need to AND ShiftModifier and LockModifier out. + mods |= basic_modifiers[ks] & ~(1< +{ +public: + static void handle(InputEvent const&); + static void initialize(); +}; + +#endif /* X11FAKEKEYBOARDHANDLER_H_ */ diff --git a/src/input/x11FakeMouseHandler.cpp b/src/input/x11FakeMouseHandler.cpp new file mode 100644 index 0000000..432e19f --- /dev/null +++ b/src/input/x11FakeMouseHandler.cpp @@ -0,0 +1,59 @@ +/* + # 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/ + # -------------------------------------------------------------------------- + # x11FakeMouseHandler.h: + # - Handle mouse events on X11 - implementation + # -------------------------------------------------------------------------- + */ + +#include +#include +#include "x11InputUtils.h" +#include "x11FakeMouseHandler.h" + +void X11FakeMouseButtonHandler::handle(InputEvent const& evt) +{ + quint16 pressedButton = evt.pressedButton(); + + Display* dpy = X11InputUtils::display(); + + XTestGrabControl(dpy, 1); + for(int i = 0; i < 16; i++) { + if((1< +{ +public: + static void handle(InputEvent const&); +}; + +class X11FakeMouseMovementHandler : public DefaultInputEventHandler +{ +public: + static void handle(InputEvent const&); +}; + +#endif /* X11FAKEMOUSEHANDLER_H_ */ diff --git a/src/input/x11InputUtils.cpp b/src/input/x11InputUtils.cpp new file mode 100644 index 0000000..b589bd6 --- /dev/null +++ b/src/input/x11InputUtils.cpp @@ -0,0 +1,29 @@ +/* + # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of + # Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # ------------------------------------------------------------------------ + # src/input/x11InputUtils.h: + # Utilities for input handling under X11 - implementation + */ + +#include "x11InputUtils.h" + +static Display* _dpy = 0; + +void X11InputUtils::setDisplay(Display* dpy) +{ + _dpy = dpy; +} + +Display* X11InputUtils::display() +{ + return _dpy; +} diff --git a/src/input/x11InputUtils.h b/src/input/x11InputUtils.h new file mode 100644 index 0000000..9c85d09 --- /dev/null +++ b/src/input/x11InputUtils.h @@ -0,0 +1,27 @@ +/* + # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of + # Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # ------------------------------------------------------------------------ + # src/input/x11InputUtils.h: + # Utilities for input handling under X11 - interface + */ + +#ifndef X11INPUTUTILS_H_ +#define X11INPUTUTILS_H_ + +#include + +struct X11InputUtils { + static void setDisplay(Display*); + static Display* display(); +}; + +#endif /* X11INPUTUTILS_H_ */ -- cgit v1.2.3-55-g7522 From 2eaaecd06784d3f0aa5542b69272bc1199ef128d Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 14:05:57 +0200 Subject: Fix forgotten NOT on test for Xkb presence --- src/input/x11FakeKeyboardHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/input') diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index bb14b76..95e83ab 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -471,7 +471,7 @@ void initialize_basic_keycodes() int xkb_opcode, xkb_event, xkb_error, xkb_major, xkb_minor; bool xkb_present = XkbQueryExtension(dpy, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor); - if(xkb_present) { + if(!xkb_present) { int keysyms_per_code; const int count = max_keycode - min_keycode + 1; KeySym* mapping = XGetKeyboardMapping(dpy, min_keycode, count, &keysyms_per_code); -- cgit v1.2.3-55-g7522 From 7fb94c8d48d70c47bb6c292cb5b58bc76b3ee376 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 14:09:40 +0200 Subject: Fix Xkb handling on certain systems. On some systems XTEST events are delivered via a special input device that may be configured with a different keymap than the physical keyboard. This patch fixes the keycode lookup to take the XTEST device into account. --- src/input/x11FakeKeyboardHandler.cpp | 77 +++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 6 deletions(-) (limited to 'src/input') diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index 95e83ab..82cc437 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "x11InputUtils.h" @@ -502,14 +503,64 @@ void initialize_basic_keycodes() } else { + // Try to find out what device XTest will send events on: + int xkbDeviceId = XkbUseCoreKbd; + Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false); + int ndevinfos; + XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); + if(devinfos) + { + 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); + + // We want it to be a keyboard. + if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard) + continue; + + int nprops; + Atom* props = XIListProperties(dpy, devinfo->deviceid, &nprops); + if(props) + { + for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++) + { + Atom prop = props[j]; + if(prop == xtestDeviceProp) + { + // The device is the XTest Keyboard: + xkbDeviceId = devinfo->deviceid; + } + } + XFree(props); + } + } + XIFreeDeviceInfo(devinfos); + } + + // Okay, we know which device to query. Now get its keymap: + XkbDescPtr keybDesc = XkbGetKeyboard(dpy, XkbAllComponentsMask, xkbDeviceId); + if(!keybDesc) + { + qWarning("Unable to retrieve keyboard description for device %d. Falling back to unreliable global mapping", xkbDeviceId); + } + for(int i = min_keycode; i <= max_keycode; i++) { for(int j = 0; j <= 0xff; j++) { KeySym ks = 0; unsigned int unconsumed; - if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) - continue; + if(keybDesc) + { + if(!XkbTranslateKeyCode(keybDesc, i, j, &unconsumed, &ks) || ks == NoSymbol) + continue; + } + else + { + if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) + continue; + } if(ks && (ks < 0x100)) { @@ -527,6 +578,12 @@ void initialize_basic_keycodes() } } } + + // Free the keyboard description: + if(keybDesc) + { + XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true); + } } // find out which keycodes cause the modifier state bits: @@ -750,10 +807,18 @@ void X11FakeKeyboardHandler::handle(InputEvent const& evt) xmodifier_type mods = translate_modifiers(evt.qt_modifiers()); // we may need to press additional modifiers to generate this keysym: - // but, since we do not differentiate upper and lower case, - // and instead let the sender handle those modifiers, - // we need to AND ShiftModifier and LockModifier out. - mods |= basic_modifiers[ks] & ~(1<button(), event->buttons())); } } @@ -271,7 +271,7 @@ void Frame::mouseReleaseEvent ( QMouseEvent * event ) ConsoleLog writeLine("Captured remote control mouseReleaseEvent"); updateMousePosition(event); - sendInputEvent(InputEvent::mousePressRelease(event)); + sendInputEvent(InputEvent::mousePressRelease(event->button(), event->buttons())); } } @@ -501,8 +501,7 @@ void Frame::keyPressEvent(QKeyEvent* event) { // The action of the keyboard may depend on the position of the pointer sendMouseMotionEvent(); - InputEvent evt = InputEvent::keyboardPress(event); - sendInputEvent(evt); + sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers())); } else { @@ -516,8 +515,7 @@ void Frame::keyReleaseEvent(QKeyEvent* event) { // The action of the keyboard may depend on the position of the pointer sendMouseMotionEvent(); - InputEvent evt = InputEvent::keyboardRelease(event); - sendInputEvent(evt); + sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers())); } else { diff --git a/src/input/inputEvent.cpp b/src/input/inputEvent.cpp index 04a84e7..9b69a3a 100644 --- a/src/input/inputEvent.cpp +++ b/src/input/inputEvent.cpp @@ -66,10 +66,10 @@ quint16 InputEvent::mouseButtonsFromQt(int b) return ret; } -InputEvent InputEvent::mousePressRelease(QMouseEvent const* evt) +InputEvent InputEvent::mousePressRelease(int qt_button, int qt_buttons) { - quint16 button = mouseButtonsFromQt(evt->button()); - quint16 buttons = mouseButtonsFromQt(evt->buttons()); + quint16 button = mouseButtonsFromQt(qt_button); + quint16 buttons = mouseButtonsFromQt(qt_buttons); quint16 code; if(buttons & button) @@ -85,14 +85,15 @@ InputEvent InputEvent::mousePressRelease(QMouseEvent const* evt) return InputEvent(ET_BUTTON, code, value); } -InputEvent InputEvent::keyboardPress(QKeyEvent const* evt) +InputEvent InputEvent::keyboardPress(int key, int mods) { - quint32 value = evt->key() | evt->modifiers(); + quint32 value = key | mods; return InputEvent(ET_KEY, EC_PRESS, value); } -InputEvent InputEvent::keyboardRelease(QKeyEvent const* evt) +InputEvent InputEvent::keyboardRelease(int key, int mods) { - quint32 value = evt->key() | evt->modifiers(); + quint32 value = key | mods; return InputEvent(ET_KEY, EC_RELEASE, value); } + diff --git a/src/input/inputEvent.h b/src/input/inputEvent.h index 917cc64..7a64bfc 100644 --- a/src/input/inputEvent.h +++ b/src/input/inputEvent.h @@ -62,9 +62,9 @@ public: static uint16_t mouseButtonsFromQt(int b); - static InputEvent mousePressRelease(QMouseEvent const* event); - static InputEvent keyboardPress(QKeyEvent const* event); - static InputEvent keyboardRelease(QKeyEvent const* event); + static InputEvent mousePressRelease(int button, int buttons); + static InputEvent keyboardPress(int key, int mods); + static InputEvent keyboardRelease(int key, int mods); static const uint16_t ET_KEY = 0; static const uint16_t ET_BUTTON = 1; @@ -76,6 +76,7 @@ public: static const uint16_t EC_REBOOT = 2; static const uint16_t EC_SYSRQ = 3; static const uint16_t EC_KILL_X = 4; + static const uint16_t EC_SAY_HELLO = 5; //< for debugging purposes typedef uint32_t event_key; -- cgit v1.2.3-55-g7522 From c5c46660130456afea285e460be44e1c723e4a49 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:07:43 +0200 Subject: Refactor InputEvent handler code. - Make static methods virtual and store instances in the chains. - Propagate security context information. - Saner security policy implementation. --- src/input/inputEventHandler.h | 108 +++++++++++++++++++++++------------ src/input/inputHandlerChain.h | 34 +++++++++++ src/input/unprivilegedHandlerChain.h | 34 ----------- src/input/x11FakeKeyboardHandler.cpp | 4 +- src/input/x11FakeKeyboardHandler.h | 4 +- src/input/x11FakeMouseHandler.cpp | 6 +- src/input/x11FakeMouseHandler.h | 4 +- src/pvs.cpp | 6 +- src/pvs.h | 2 + 9 files changed, 121 insertions(+), 81 deletions(-) create mode 100644 src/input/inputHandlerChain.h delete mode 100644 src/input/unprivilegedHandlerChain.h (limited to 'src/input') diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 3910f93..330f5a7 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -27,49 +27,80 @@ #define HANDLER_CODE_DONT_CARE 0xffff #define HANDLER_VALUE_DONT_CARE 0xffffffff +class InputEventContext +{ +public: + virtual pid_t getSenderPid() const = 0; + virtual uid_t getSenderUid() const = 0; + virtual gid_t getSenderGid() const = 0; +}; + +struct SpecialInputEventDescription +{ + SpecialInputEventDescription(QString const& d, quint16 t, quint16 c, quint32 v = 0) + : descriptionString(d), evtType(t), evtCode(c), evtValue(v) + { + } + + QString descriptionString; + quint16 evtType; + quint16 evtCode; + quint32 evtValue; + + InputEvent toEvent() const + { + return InputEvent(evtType, evtCode, evtValue); + } +}; + template class DefaultInputEventHandler { public: - static bool matches(InputEvent const& evt) { - if(Type != 0xffff) { + virtual bool matches(InputEvent const& evt, InputEventContext const*) { + if(Type != HANDLER_TYPE_DONT_CARE) { if(evt.type() != Type) return false; } - if(Code != 0xffff) { + if(Code != HANDLER_CODE_DONT_CARE) { if(evt.code() != Code) return false; } - if(Value != 0xffffffff) { + if(Value != HANDLER_VALUE_DONT_CARE) { if(evt.value() != Value) return false; } return true; } - static void initialize() + virtual void initialize() { } -}; -namespace policy { + virtual void handle(InputEvent const& evt, InputEventContext const*) = 0; -struct NoSecurityCheck { - static bool allow(InputEvent const&) { - return true; + static void describeInto(QList& description) + { } }; -struct PhysicalSeatSecurityCheck { - static bool allow(InputEvent const&) { - return /* TODO implement */ true; - } +namespace policy { + +enum SecurityFlags { + SEC_PHYSICAL_SEAT = 1, + SEC_PRIVILEGED_USER = 2 }; -struct AlwaysDenySecurityCheck { - static bool allow(InputEvent const&) { - return false; +bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx); +bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx); + +template +struct Security +{ + bool allow(InputEvent const& evt, InputEventContext const* ctx) + { + return true; } }; @@ -107,39 +138,43 @@ template class HandlerHelper { public: - static bool handle(InputEvent const& evt) { - if(!SecurityPolicy::allow(evt)) + bool handle(InputEvent const& evt, InputEventContext const* context = 0) { + if(!securityPolicy.allow(evt, context)) { return true; } - if(Delegate::matches(evt)) { - Delegate::handle(evt); + if(delegate.matches(evt, context)) { + delegate.handle(evt, context); return true; } else { return false; } } - static void initialize() + void initialize() { - Delegate::initialize(); + delegate.initialize(); } + +private: + Delegate delegate; + SecurityPolicy securityPolicy; }; template class HandlerHelper { public: - static bool handle(InputEvent const& evt) { + bool handle(InputEvent const& evt, InputEventContext const* context = 0) { return false; } - static void initialize() + void initialize() { } }; -template +template > struct Handler : public HandlerHelper { }; @@ -153,28 +188,31 @@ private: typedef typename boost::mpl::deref::type handler_type; + handler_type _handler; + next_in_chain _next; + public: - static void handle(InputEvent const& evt) { - if(!handler_type::handle(evt)) { - next_in_chain::handle(evt); + void handle(InputEvent const& evt, InputEventContext const* context = 0) { + if(!_handler.handle(evt, context)) { + _next.handle(evt, context); } } - static void initialize() { - handler_type::initialize(); - next_in_chain::initialize(); + void initialize() { + _handler.initialize(); + _next.initialize(); + } } }; template struct InputEventHandlerChainHelper { -public: - static void handle(InputEvent const&) { + void handle(InputEvent const&, InputEventContext const* context = 0) { // do nothing } - static void initialize() { + void initialize() { // do nothing } }; diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h new file mode 100644 index 0000000..4bb9fe5 --- /dev/null +++ b/src/input/inputHandlerChain.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/ + # -------------------------------------------------------------------------- + # inputHandlerChain.h: + # - Definition of the input handler chain + # -------------------------------------------------------------------------- + */ + +#ifndef INPUTHANDLERCHAIN_H_ +#define INPUTHANDLERCHAIN_H_ + +#include +#include "inputEventHandler.h" + +#include "x11FakeKeyboardHandler.h" +#include "x11FakeMouseHandler.h" + +typedef boost::mpl::list< + Handler >, + Handler >, + Handler > +>::type unprivileged_handler_list; + +typedef InputEventHandlerChain unprivileged_handler_chain; + +#endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/unprivilegedHandlerChain.h b/src/input/unprivilegedHandlerChain.h deleted file mode 100644 index 734720a..0000000 --- a/src/input/unprivilegedHandlerChain.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - # 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: - # - Definition of the input handler chain - # -------------------------------------------------------------------------- - */ - -#ifndef INPUTHANDLERCHAIN_H_ -#define INPUTHANDLERCHAIN_H_ - -#include -#include "inputEventHandler.h" - -#include "x11FakeKeyboardHandler.h" -#include "x11FakeMouseHandler.h" - -typedef boost::mpl::list< - Handler >, - Handler >, - Handler > ->::type unprivileged_handler_list; - -typedef InputEventHandlerChain unprivileged_handler_chain; - -#endif /* INPUTHANDLERCHAIN_H_ */ diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index 82cc437..3a0b864 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -20,6 +20,7 @@ // Qt headers need to be included before X11 headers #include #include +#include "x11FakeKeyboardHandler.h" // #include #include #include @@ -30,7 +31,6 @@ #include #include #include "x11InputUtils.h" -#include "x11FakeKeyboardHandler.h" //////////////////////// INPUT EVENT TRANSLATION ///////////////////////////////// @@ -766,7 +766,7 @@ void X11FakeKeyboardHandler::initialize() current_modifiers = 0; } -void X11FakeKeyboardHandler::handle(InputEvent const& evt) +void X11FakeKeyboardHandler::handle(InputEvent const& evt, InputEventContext const*) { Display* dpy = X11InputUtils::display(); diff --git a/src/input/x11FakeKeyboardHandler.h b/src/input/x11FakeKeyboardHandler.h index c888202..3dde7cc 100644 --- a/src/input/x11FakeKeyboardHandler.h +++ b/src/input/x11FakeKeyboardHandler.h @@ -22,8 +22,8 @@ class X11FakeKeyboardHandler : public DefaultInputEventHandler { public: - static void handle(InputEvent const&); - static void initialize(); + void handle(InputEvent const&, InputEventContext const* = 0); + void initialize(); }; #endif /* X11FAKEKEYBOARDHANDLER_H_ */ diff --git a/src/input/x11FakeMouseHandler.cpp b/src/input/x11FakeMouseHandler.cpp index 432e19f..58415d5 100644 --- a/src/input/x11FakeMouseHandler.cpp +++ b/src/input/x11FakeMouseHandler.cpp @@ -14,12 +14,12 @@ # -------------------------------------------------------------------------- */ +#include "x11FakeMouseHandler.h" // need to include before X headers #include #include #include "x11InputUtils.h" -#include "x11FakeMouseHandler.h" -void X11FakeMouseButtonHandler::handle(InputEvent const& evt) +void X11FakeMouseButtonHandler::handle(InputEvent const& evt, InputEventContext const*) { quint16 pressedButton = evt.pressedButton(); @@ -42,7 +42,7 @@ void X11FakeMouseButtonHandler::handle(InputEvent const& evt) XFlush(dpy); } -void X11FakeMouseMovementHandler::handle(InputEvent const& evt) +void X11FakeMouseMovementHandler::handle(InputEvent const& evt, InputEventContext const*) { ConsoleLog writeLine(QString("Received mouse motion event (%1,%2)").arg(evt.xCoord()).arg(evt.yCoord())); Display* dpy = X11InputUtils::display(); diff --git a/src/input/x11FakeMouseHandler.h b/src/input/x11FakeMouseHandler.h index 19ed29e..0e32256 100644 --- a/src/input/x11FakeMouseHandler.h +++ b/src/input/x11FakeMouseHandler.h @@ -22,13 +22,13 @@ class X11FakeMouseButtonHandler : public DefaultInputEventHandler { public: - static void handle(InputEvent const&); + void handle(InputEvent const&, InputEventContext const* = 0); }; class X11FakeMouseMovementHandler : public DefaultInputEventHandler { public: - static void handle(InputEvent const&); + void handle(InputEvent const&, InputEventContext const* = 0); }; #endif /* X11FAKEMOUSEHANDLER_H_ */ diff --git a/src/pvs.cpp b/src/pvs.cpp index e44d04d..911ed0d 100644 --- a/src/pvs.cpp +++ b/src/pvs.cpp @@ -16,7 +16,7 @@ #include "src/net/pvsServiceDiscovery.h" #include "src/net/pvsDiscoveredServer.h" #include "src/input/inputEvent.h" -#include "src/input/unprivilegedHandlerChain.h" +#include "src/input/inputHandlerChain.h" #include "src/input/x11InputUtils.h" // D-Bus @@ -643,11 +643,11 @@ void PVS::handleInputEvent(InputEvent const& evt) { std::string s = evt.toString(); ConsoleLog writeLine(QString("Received input event: %1").arg(s.c_str())); - unprivileged_handler_chain::handle(evt); + _inputEventHandlers.handle(evt); } void PVS::initializeInputEventHandling() { X11InputUtils::setDisplay(X11Info::display()); - unprivileged_handler_chain::initialize(); + _inputEventHandlers.initialize(); } diff --git a/src/pvs.h b/src/pvs.h index 8e94169..f4e53c0 100644 --- a/src/pvs.h +++ b/src/pvs.h @@ -24,6 +24,7 @@ #include "src/version.h" #include "src/util/consoleLogger.h" #include "src/util/clientGUIUtils.h" +#include "src/input/inputHandlerChain.h" class PVSServiceDiscovery; @@ -144,6 +145,7 @@ private: int _timerLockDelay; // input event handling: + unprivileged_handler_chain _inputEventHandlers; void handleInputEvent(InputEvent const& evt); void initializeInputEventHandling(); }; -- cgit v1.2.3-55-g7522 From ee159229bcfb6f2dbd19ef16e37fa2c65cd3846d Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:15:36 +0200 Subject: Add Permission checking and session information code. --- src/input/inputEventHandler.cpp | 36 +++++ src/input/inputEventHandler.h | 4 + src/input/pvsCheckPrivileges.cpp | 314 +++++++++++++++++++++++++++++++++++++++ src/input/pvsCheckPrivileges.h | 139 +++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 src/input/inputEventHandler.cpp create mode 100644 src/input/pvsCheckPrivileges.cpp create mode 100644 src/input/pvsCheckPrivileges.h (limited to 'src/input') diff --git a/src/input/inputEventHandler.cpp b/src/input/inputEventHandler.cpp new file mode 100644 index 0000000..c16c358 --- /dev/null +++ b/src/input/inputEventHandler.cpp @@ -0,0 +1,36 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # inputEventHandler.h: + # - Common definitions for input event handlers - implementation + # -------------------------------------------------------------------------- + */ + +#include "inputEventHandler.h" +#include "pvsCheckPrivileges.h" + +bool policy::allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx) +{ + if(ctx) + return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_UNKNOWN, PVSCheckPrivileges::USER_PRIVILEGED, + ctx); + else + return false; +} + +bool policy::allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx) +{ + if(ctx) + return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_LOCAL, PVSCheckPrivileges::USER_UNKNOWN, + ctx); + else + return false; +} diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 330f5a7..a0ada2e 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -100,6 +100,10 @@ struct Security { bool allow(InputEvent const& evt, InputEventContext const* ctx) { + if((flags & SEC_PHYSICAL_SEAT) && !allowPhysicalSeat(evt, ctx)) + return false; + if((flags & SEC_PRIVILEGED_USER) && !allowPrivilegedUser(evt, ctx)) + return false; return true; } }; diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp new file mode 100644 index 0000000..56073bc --- /dev/null +++ b/src/input/pvsCheckPrivileges.cpp @@ -0,0 +1,314 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # pvsCheckPrivileges.cpp: + # - A small program that checks whether or not it is called from + # a physical seat and conditionally executes the pvs input daemon. + # Additional security-relevant conditions should be checked here. + # + # The program is called with exactly one parameter, specifying the + # number of the file descriptor which is to be passed to its child. + # -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pvsCheckPrivileges.h" + +using namespace std; + +#define TIMEOUT_VALUE 10000 /* Wait for max. 10 seconds */ + +// We need these classes for PolicyKit access: +struct PolKitSubject { + QString subject_kind; + QMap subject_details; +}; +Q_DECLARE_METATYPE(PolKitSubject); + +QDBusArgument& operator<<(QDBusArgument& arg, PolKitSubject const& subj) +{ + arg.beginStructure(); + arg << subj.subject_kind << subj.subject_details; + arg.endStructure(); + return arg; +} + +QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitSubject& subj) +{ + arg.beginStructure(); + arg >> subj.subject_kind >> subj.subject_details; + arg.endStructure(); + return arg; +} + +struct PolKitAuthReply { + bool isAuthorized; + bool isChallenge; + QMap details; +}; +Q_DECLARE_METATYPE(PolKitAuthReply); + +QDBusArgument& operator<<(QDBusArgument& arg, PolKitAuthReply const& reply) +{ + arg.beginStructure(); + arg << reply.isAuthorized << reply.isChallenge << reply.details; + arg.endStructure(); + return arg; +} + +QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitAuthReply& reply) +{ + arg.beginStructure(); + arg >> reply.isAuthorized >> reply.isChallenge >> reply.details; + arg.endStructure(); + return arg; +} + +// We need to pass QMap to QVariant: +typedef QMap QStringStringMap; +Q_DECLARE_METATYPE(QStringStringMap) + +PVSCheckPrivileges* PVSCheckPrivileges::instance() +{ + static PVSCheckPrivileges static_instance; + return &static_instance; +} + +PVSCheckPrivileges::PVSCheckPrivileges() +{ +} + +PVSCheckPrivileges::~PVSCheckPrivileges() +{ +} + +QString PVSCheckPrivileges::getSessionReference(CachedInputContext const& sender) +{ + if(!sender.isValid()) + { + return QString(); + } + + QString sessionReference = _savedConsoleKitSession.value(sender, QString()); + if(sessionReference.isNull()) + { + QDBusConnection conn = QDBusConnection::systemBus(); + // Find the name of the current session: + QDBusInterface manager("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", conn); + QDBusReply replyGetSession = manager.call(QDBus::Block, "GetSessionForUnixProcess", (quint32)sender.pid); + if(!replyGetSession.isValid()) + { + qWarning("Reply to GetSessionForUnixProcess is invalid: %s: %s", replyGetSession.error().name().toLocal8Bit().constData(), replyGetSession.error().message().toLocal8Bit().constData()); + return QString(); + } + _savedConsoleKitSession[sender] = sessionReference = replyGetSession.value().path(); + } + return sessionReference; +} + +PVSCheckPrivileges::SessionKind PVSCheckPrivileges::getSessionKind(CachedInputContext const& sender) +{ + if(!sender.isValid()) + { + return SESSION_UNKNOWN; + } + + // if the sender is root, we always suppose he works locally. + if(sender.uid == 0) + { + return SESSION_LOCAL; + } + + QString sessionReference = getSessionReference(sender); + if(sessionReference.isNull()) + { + return SESSION_LOOKUP_FAILURE; + } + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface session("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn); + QDBusReply replyIsLocal = session.call(QDBus::Block, "IsLocal"); + + if(!replyIsLocal.isValid()) + { + qWarning("Unable to find out whether the current session is local: %s: %s", replyIsLocal.error().name().toLocal8Bit().constData(), replyIsLocal.error().message().toLocal8Bit().constData()); + return SESSION_LOOKUP_FAILURE; + } + return replyIsLocal.value() ? SESSION_LOCAL : SESSION_NONLOCAL; +} + +PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInputContext const& sender) +{ + // Always allow root: + if(sender.uid == 0) + return USER_PRIVILEGED; + + // For PolKit, we need the start-time of the process. + // On Linux, we can get it from /proc: + QString procStat = QString("/proc/%1/stat").arg(sender.pid); + QFile procStatFile(procStat); + if(!procStatFile.exists()) + { + qWarning("Could not look up any info on process %d, its %s file does not exist", sender.pid, procStat.toLocal8Bit().constData()); + return USER_LOOKUP_FAILURE; + } + procStatFile.open(QIODevice::ReadOnly); + QByteArray procStatBytes = procStatFile.readAll(); + qDebug() << "Read stat file: " << procStatBytes; + QString procStatLine = QString::fromLocal8Bit(procStatBytes.constData(), procStatBytes.length()); + QStringList procStatFields = procStatLine.split(QRegExp("\\s+")); + qDebug() << "Found stat fields: " << procStatFields; + bool ok; + quint64 startTime = procStatFields[21].toULongLong(&ok); + if(!ok) + { + qWarning("Could not find out start time for process %d", (int)sender.pid); + return USER_LOOKUP_FAILURE; + } + + // Okay, we got the startTime. Now ask PolicyKit: + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface intf("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", conn); + PolKitSubject subj; + subj.subject_kind = "unix-process"; + subj.subject_details["pid"] = (quint32)sender.pid; + subj.subject_details["start-time"] = startTime; + QDBusReply reply = intf.call(QDBus::Block, + QLatin1String("CheckAuthorization"), + QVariant::fromValue(subj), + "org.openslx.pvs.privilegedinput", + QVariant::fromValue(QMap()) /* No details */, + (quint32)1 /* Allow Interaction */, + QUuid::createUuid().toString() /* Cancellation ID */); + if(!reply.isValid()) + { + QDBusError err = reply.error(); + + qWarning("Reply to CheckAuthorization is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData()); + return USER_LOOKUP_FAILURE; + } + return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED; +} + +QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender) +{ + QString sessionReference = getSessionReference(sender); + if(sessionReference.isNull()) + { + return QString(); + } + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn); + QDBusReply reply = intf.call(QDBus::Block, QLatin1String("GetX11Display")); + if(!reply.isValid()) + { + QDBusError err = reply.error(); + + qWarning("Reply to GetX11Display is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData()); + return QString(); + } + + return reply.value(); +} + +QString PVSCheckPrivileges::getX11DisplayDevice(CachedInputContext const& sender) +{ + QString sessionReference = getSessionReference(sender); + if(sessionReference.isNull()) + { + return QString(); + } + + QDBusConnection conn = QDBusConnection::systemBus(); + QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn); + QDBusReply reply = intf.call(QDBus::Block, QLatin1String("GetX11DisplayDevice")); + if(!reply.isValid()) + { + QDBusError err = reply.error(); + + qWarning("Reply to GetX11DisplayDevice is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData()); + return QString(); + } + + return reply.value(); +} + +bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext const& sender) +{ + SessionKind cachedSessionKind; + + if(sessionKind < SESSION_NONLOCAL) + { + if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN) + { + cachedSessionKind = getSessionKind(sender); + if(cachedSessionKind != SESSION_LOOKUP_FAILURE) + _savedSessionKind[sender] = cachedSessionKind; + qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData()); + } + if(cachedSessionKind > sessionKind) + return false; + } + + return true; +} + +bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender) +{ + UserPrivilege cachedUserPrivilege; + + if(userPrivilege < USER_UNPRIVILEGED) + { + if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN) + { + cachedUserPrivilege = getUserPrivilege(sender); + if(cachedUserPrivilege != USER_LOOKUP_FAILURE) + _savedUserPrivilege[sender] = cachedUserPrivilege; + qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData()); + } + if(cachedUserPrivilege > userPrivilege) + return false; + } + return true; +} + +bool PVSCheckPrivileges::require(SessionKind sessionKind, + UserPrivilege userPrivilege, + CachedInputContext const& sender) +{ + if(!require(sessionKind, sender)) + return false; + if(!require(userPrivilege, sender)) + return false; + return true; +} + +uint qHash(CachedInputContext const& p) +{ + return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid))); +} diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h new file mode 100644 index 0000000..e5cc9a4 --- /dev/null +++ b/src/input/pvsCheckPrivileges.h @@ -0,0 +1,139 @@ +/* + # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of + # Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # ----------------------------------------------------------------------------- + # src/net/pvsCheckPrivileges_linux.h + # - Linux implementation of privilege checking + # ----------------------------------------------------------------------------- + */ + +#ifndef PVSCHECKPRIVILEGES_H_ +#define PVSCHECKPRIVILEGES_H_ + +#include +#include +#include +#include "inputEventHandler.h" + +struct CachedInputContext +{ + CachedInputContext(InputEventContext const* source) + { + if(source) + { + pid = source->getSenderPid(); + uid = source->getSenderUid(); + gid = source->getSenderGid(); + } + else + { + pid = (pid_t)-1; + uid = (uid_t)-1; + gid = (gid_t)-1; + } + } + + CachedInputContext() + { + pid = (pid_t)-1; + uid = (uid_t)-1; + gid = (gid_t)-1; + } + + pid_t pid; + uid_t uid; + gid_t gid; + + bool isValid() const + { + return (pid != (pid_t)-1) && (uid != (uid_t)-1) && (gid != (gid_t)-1); + } + + bool operator==(CachedInputContext const& other) const + { + return (other.pid == pid) && (other.uid == uid) && (other.gid == gid); + } +}; +uint qHash(CachedInputContext const& p); + +class PVSCheckPrivileges +{ +public: + typedef enum { + SESSION_LOOKUP_FAILURE, // Comes first because we default to assume + // the session is local if we cannot look it + // up. + SESSION_LOCAL, + SESSION_NONLOCAL, + SESSION_UNKNOWN + } SessionKind; + static QString toString(SessionKind k) + { + switch(k) + { + case SESSION_LOOKUP_FAILURE: return "SESSION_LOOKUP_FAILURE"; + case SESSION_LOCAL: return "SESSION_LOCAL"; + case SESSION_NONLOCAL: return "SESSION_NONLOCAL"; + case SESSION_UNKNOWN: return "SESSION_UNKNOWN"; + default: return QString("unknown value (%1)").arg(k); + } + } + + typedef enum { + USER_PRIVILEGED, + USER_UNPRIVILEGED, + USER_LOOKUP_FAILURE, // Comes last because we default to assume + // the user is unprivileged if we cannot get + // permission from PolicyKit. + USER_UNKNOWN + } UserPrivilege; + static QString toString(UserPrivilege k) + { + switch(k) + { + case USER_PRIVILEGED: return "USER_PRIVILEGED"; + case USER_UNPRIVILEGED: return "USER_UNPRIVILEGED"; + case USER_LOOKUP_FAILURE: return "USER_LOOKUP_FAILURE"; + case USER_UNKNOWN: return "USER_UNKNOWN"; + default: return QString("unknown value (%1)").arg(k); + } + } + + static PVSCheckPrivileges* instance(); + + bool require(SessionKind sessionKind, CachedInputContext const& sender); + bool require(UserPrivilege userPrivilege, CachedInputContext const& sender); + bool require(SessionKind sessionKind, UserPrivilege userPrivilege, CachedInputContext const& sender); + QString getX11SessionName(CachedInputContext const& sender); + QString getX11DisplayDevice(CachedInputContext const& sender); + +private: + PVSCheckPrivileges(); + virtual ~PVSCheckPrivileges(); + + typedef QPair > piduidgid; + piduidgid makePidUidGid(pid_t pid, uid_t uid, gid_t gid) + { + return qMakePair(pid, qMakePair(uid, gid)); + } + + QString getSessionReference(CachedInputContext const& sender); + SessionKind getSessionKind(CachedInputContext const& sender); + UserPrivilege getUserPrivilege(CachedInputContext const& sender); + + static PVSCheckPrivileges* _instance; + + QHash _savedUserPrivilege; + QHash _savedSessionKind; + QHash _savedConsoleKitSession; +}; + +#endif /* PVSCHECKPRIVILEGES_H_ */ -- cgit v1.2.3-55-g7522 From e3dcd9bc390bab4e650ec5bcbc1720cdcdea0247 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 15:26:13 +0200 Subject: Add description to input event handlers so they can --- src/input/inputEventHandler.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'src/input') diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index a0ada2e..44713c2 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -18,6 +18,9 @@ #define INPUTEVENTHANDLER_H_ #include +#include +#include +#include #include #include #include @@ -57,6 +60,12 @@ template class DefaultInputEventHandler { +protected: + static QString tr(char const* string) + { + return QCoreApplication::translate("InputEventHandler", string); + } + public: virtual bool matches(InputEvent const& evt, InputEventContext const*) { if(Type != HANDLER_TYPE_DONT_CARE) { @@ -160,6 +169,11 @@ public: delegate.initialize(); } + static void describeInto(QList& list) + { + Delegate::describeInto(list); + } + private: Delegate delegate; SecurityPolicy securityPolicy; @@ -176,6 +190,10 @@ public: void initialize() { } + + static void describeInto(QList&) + { + } }; template > @@ -206,6 +224,18 @@ public: _handler.initialize(); _next.initialize(); } + + static void describeInto(QList& list) + { + handler_type::describeInto(list); + next_in_chain::describeInto(list); + } + + static QList describe() + { + QList list; + describeInto(list); + return list; } }; @@ -219,6 +249,16 @@ struct InputEventHandlerChainHelper void initialize() { // do nothing } + + static void describeInto(QList&) + { + // do nothing + } + + static QList describe() + { + return QList(); + } }; template -- 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') 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') 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') 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') 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') 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 e753c26e49e47e24ab2417b775815bf035bd5baf Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Tue, 5 Oct 2010 23:00:14 +0200 Subject: Fix recognition of letters in keyboard handler --- src/input/x11FakeKeyboardHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/input') diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index e7a5826..b1492e7 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -848,7 +848,7 @@ void X11FakeKeyboardHandler::handle(InputEvent const& evt, InputEventContext con xmodifier_type mods = translate_modifiers(evt.qt_modifiers()); // we may need to press additional modifiers to generate this keysym: - if(isalpha(ks)) + if(QChar(ks, 0).isLetter()) { // but, since we do not differentiate upper and lower case, // and instead let the sender handle those modifiers, -- cgit v1.2.3-55-g7522 From 76756513af78ff0c96cdbcad91727f26f48cc922 Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 00:05:54 +0200 Subject: Fix typo that prevents correct compilation when XInput2.h is present --- src/input/x11FakeKeyboardHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/input') diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp index b1492e7..0c32b66 100644 --- a/src/input/x11FakeKeyboardHandler.cpp +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -545,7 +545,7 @@ void initialize_basic_keycodes() XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); if(devinfos) { -#ifndef HAVE_INPUT2_H +#ifndef HAVE_XINPUT2_H # define deviceid id #endif for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++) -- 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') 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 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') 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') 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 c5c91f40c02d7b8611fb5e479f340fe19254592f Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Wed, 6 Oct 2010 15:09:43 +0200 Subject: Fix deletion order bug PVSCheckPrivileges::instance() is statically allocated. When it is deleted, the QCoreApplication is already gone (since it is stack- allocated), and the destructor of QFileSystemWatcher waits forever. --- src/input/pvsCheckPrivileges.cpp | 13 +++++++++++-- src/input/pvsCheckPrivileges.h | 1 + src/input/pvsprivinputd.cpp | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src/input') diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index 71c5861..57b5d8b 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -100,8 +100,17 @@ Q_DECLARE_METATYPE(QStringStringMap) PVSCheckPrivileges* PVSCheckPrivileges::instance() { - static PVSCheckPrivileges static_instance; - return &static_instance; + static PVSCheckPrivileges* static_instance = 0; + if(!static_instance) + { + static_instance = new PVSCheckPrivileges(); + } + return static_instance; +} + +void PVSCheckPrivileges::deleteInstance() +{ + delete instance(); } PVSCheckPrivileges::PVSCheckPrivileges(QObject* parent) diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h index fc82e4c..ec6c591 100644 --- a/src/input/pvsCheckPrivileges.h +++ b/src/input/pvsCheckPrivileges.h @@ -114,6 +114,7 @@ public: } static PVSCheckPrivileges* instance(); + static void deleteInstance(); bool require(SessionKind sessionKind, CachedInputContext const& sender); bool require(UserPrivilege userPrivilege, CachedInputContext const& sender); diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp index 361bf9f..2f19d90 100644 --- a/src/input/pvsprivinputd.cpp +++ b/src/input/pvsprivinputd.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "pvsCheckPrivileges.h" #include "pvsPrivInputSocket.h" #include "pvsPrivInputHandler.h" #include "pvsPrivInputSignalHandler.h" @@ -151,5 +152,7 @@ int main(int argc, char** argv) // and run the main loop. int ret = app.exec(); + + PVSCheckPrivileges::deleteInstance(); return ret; } -- 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') 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') 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') 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') 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') 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') 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 From 958d63d244a3f4a4542c529761ccdd9cd8244fcf Mon Sep 17 00:00:00 2001 From: Sebastien Braun Date: Thu, 7 Oct 2010 09:29:06 +0200 Subject: Make behaviour on lookup failures configurable --- src/input/pvsCheckPrivileges.cpp | 78 ++++++++++++++++++++++++++++++++++++---- src/input/pvsCheckPrivileges.h | 8 ++--- src/input/pvsprivinputd.conf | 16 ++++++++- 3 files changed, 88 insertions(+), 14 deletions(-) (limited to 'src/input') diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp index f9a8851..2026c0a 100644 --- a/src/input/pvsCheckPrivileges.cpp +++ b/src/input/pvsCheckPrivileges.cpp @@ -311,7 +311,12 @@ bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext con { SessionKind cachedSessionKind; - if(sessionKind < SESSION_NONLOCAL) + if(sessionKind == SESSION_NONLOCAL) + { + // All sessions are at least non-local + return true; + } + else if(sessionKind == SESSION_LOCAL) { if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN) { @@ -320,18 +325,49 @@ bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext con _savedSessionKind[sender] = cachedSessionKind; qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData()); } - if(cachedSessionKind > sessionKind) + + switch(cachedSessionKind) + { + case SESSION_LOOKUP_FAILURE: + case SESSION_UNKNOWN: + { + // If we cannot find out the correct session kind, look up what we should do in + // the configuration: + QSettings* config = pvsPrivInputGetSettings(); + QVariant assumeLocal = config->value("assume-session-local", false); + if(!assumeLocal.canConvert(QVariant::Bool)) + { + qWarning("There is an assume-session-local setting, but cannot convert it to boolean"); + return false; + } + return assumeLocal.toBool(); + } + case SESSION_LOCAL: + return true; + case SESSION_NONLOCAL: return false; + default: + qWarning("Internal error: Undefined session kind %d", (int)cachedSessionKind); + return false; + } + } + else + { + qWarning("Internal error: It does not make sense to require an unknown session or undefined session kind %d", (int)sessionKind); + return false; } - - return true; } bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender) { UserPrivilege cachedUserPrivilege; - if(userPrivilege < USER_UNPRIVILEGED) + if(userPrivilege == USER_UNPRIVILEGED) + { + // All users are unprivileged + return true; + } + else if(userPrivilege == USER_PRIVILEGED) { if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN) { @@ -340,10 +376,38 @@ bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext _savedUserPrivilege[sender] = cachedUserPrivilege; qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData()); } - if(cachedUserPrivilege > userPrivilege) + + switch(cachedUserPrivilege) + { + case USER_LOOKUP_FAILURE: + case USER_UNKNOWN: + { + // If we cannot find out the correct user privilege level, look up what we should do in + // the configuration: + QSettings* config = pvsPrivInputGetSettings(); + QVariant assumePrivileged = config->value("assume-user-privileged", false); + if(!assumePrivileged.canConvert(QVariant::Bool)) + { + qWarning("There is an assume-session-local setting, but cannot convert it to boolean"); + return false; + } + return assumePrivileged.toBool(); + } + case USER_PRIVILEGED: + return true; + case USER_UNPRIVILEGED: + return false; + default: + qWarning("Internal error: Found undefined user privilege level %d", (int)cachedUserPrivilege); + _savedUserPrivilege.remove(sender); return false; + } + } + else + { + qWarning("Internal error: It does not make sense to require an unknown or undefined user privilege level %d", (int)userPrivilege); + return false; } - return true; } bool PVSCheckPrivileges::require(SessionKind sessionKind, diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h index ec6c591..62b463c 100644 --- a/src/input/pvsCheckPrivileges.h +++ b/src/input/pvsCheckPrivileges.h @@ -74,11 +74,9 @@ class PVSCheckPrivileges : public QObject Q_OBJECT public: typedef enum { - SESSION_LOOKUP_FAILURE, // Comes first because we default to assume - // the session is local if we cannot look it - // up. SESSION_LOCAL, SESSION_NONLOCAL, + SESSION_LOOKUP_FAILURE, SESSION_UNKNOWN } SessionKind; static QString toString(SessionKind k) @@ -96,9 +94,7 @@ public: typedef enum { USER_PRIVILEGED, USER_UNPRIVILEGED, - USER_LOOKUP_FAILURE, // Comes last because we default to assume - // the user is unprivileged if we cannot get - // permission from PolicyKit. + USER_LOOKUP_FAILURE, USER_UNKNOWN } UserPrivilege; static QString toString(UserPrivilege k) diff --git a/src/input/pvsprivinputd.conf b/src/input/pvsprivinputd.conf index 52df206..a62a922 100644 --- a/src/input/pvsprivinputd.conf +++ b/src/input/pvsprivinputd.conf @@ -11,4 +11,18 @@ 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 + +;; assume-session-local: +;; Assume that a session is local if it can not be looked up in ConsoleKit, +;; for example, if you are not running ConsoleKit. +;; +;; WARNING: Setting this to true may be a security risk. Running ConsoleKit is +;; really recommended. +; assume-session-local = false + +;; assume-user-privileged: +;; Assume that a user is privileged if he/she can not be looked up in +;; the user database or PolicyKit fails to deliver an answer. +;; +;; WARNING: Setting this to true is most definitely a security risk. +; assume-user-privileged = false \ No newline at end of file -- cgit v1.2.3-55-g7522