Merge pull request #7030 from symless/SYNERGY-1017-Secure-input-detection-on-mac
SYNERGY-1017 - Secure input detection on mac
This commit is contained in:
commit
13d0df7b44
5 changed files with 122 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ Enhancements:
|
||||||
- #6998 Remove functionality related to the screen saver synchronisation
|
- #6998 Remove functionality related to the screen saver synchronisation
|
||||||
- #7026 Update the way we package OpenSSL for Windows build
|
- #7026 Update the way we package OpenSSL for Windows build
|
||||||
- #7037 Add Ubuntu 21.04 build
|
- #7037 Add Ubuntu 21.04 build
|
||||||
|
- #7030 Add user nofication for secure input on Mac
|
||||||
===========
|
===========
|
||||||
|
|
||||||
v1.14.0-stable
|
v1.14.0-stable
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,42 @@ void MainWindow::saveSettings()
|
||||||
GUI::Config::ConfigWriter::make()->globalSave();
|
GUI::Config::ConfigWriter::make()->globalSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::checkSystemInterruptions()
|
||||||
|
{
|
||||||
|
if(synergyType() == synergyServer)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
if(!isOSXSecureInputEnabled())
|
||||||
|
{
|
||||||
|
m_isSecureInputNotificationShown = false;
|
||||||
|
}
|
||||||
|
else if(!m_isSecureInputNotificationShown)
|
||||||
|
{
|
||||||
|
// avoid showing multiple duplicate messages
|
||||||
|
m_isSecureInputNotificationShown = true;
|
||||||
|
|
||||||
|
QMessageBox message(this);
|
||||||
|
message.addButton(QObject::tr("Accept"), QMessageBox::AcceptRole);
|
||||||
|
std::string messageText =
|
||||||
|
"Secure input was enabled in your system by another application. " \
|
||||||
|
"Synergy will not be able to send keyboard strokes while the secure input is enabled\n\n";
|
||||||
|
int secureInputProcessPID = getOSXSecureInputEventPID();
|
||||||
|
std::string infringingProcessName = getOSXProcessName(secureInputProcessPID);
|
||||||
|
|
||||||
|
// IO registry may not contain the secure input process PID
|
||||||
|
// in this case don't add an option to quit the infringing app
|
||||||
|
if(secureInputProcessPID == 0) infringingProcessName = "unknown";
|
||||||
|
else message.addButton(QString("Quit %1").arg(infringingProcessName.c_str()), QMessageBox::ApplyRole);
|
||||||
|
messageText += "Infringing process is " + infringingProcessName;
|
||||||
|
message.setText(QObject::tr(messageText.c_str()));
|
||||||
|
|
||||||
|
// if user decides to stop the app send the SIGTERM signal
|
||||||
|
if (message.exec() == QMessageBox::Accepted && secureInputProcessPID) kill(secureInputProcessPID, SIGTERM);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::zeroConfToggled() {
|
void MainWindow::zeroConfToggled() {
|
||||||
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
||||||
updateZeroconfService();
|
updateZeroconfService();
|
||||||
|
|
@ -707,6 +743,7 @@ void MainWindow::startSynergy()
|
||||||
connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus)));
|
connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus)));
|
||||||
connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput()));
|
connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput()));
|
||||||
connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError()));
|
connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError()));
|
||||||
|
connect(&m_systemInterruptionCheckTimer, SIGNAL(timeout()), this, SLOT(checkSystemInterruptions()));
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << args;
|
qDebug() << args;
|
||||||
|
|
@ -1024,6 +1061,7 @@ void MainWindow::setSynergyState(qSynergyState state)
|
||||||
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
|
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
|
||||||
m_pButtonToggleStart->setText(tr("&Stop"));
|
m_pButtonToggleStart->setText(tr("&Stop"));
|
||||||
m_pButtonApply->setEnabled(true);
|
m_pButtonApply->setEnabled(true);
|
||||||
|
m_systemInterruptionCheckTimer.start(5000); // check every 5 seconds
|
||||||
}
|
}
|
||||||
else if (state == synergyDisconnected)
|
else if (state == synergyDisconnected)
|
||||||
{
|
{
|
||||||
|
|
@ -1031,6 +1069,7 @@ void MainWindow::setSynergyState(qSynergyState state)
|
||||||
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
|
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
|
||||||
m_pButtonToggleStart->setText(tr("&Start"));
|
m_pButtonToggleStart->setText(tr("&Start"));
|
||||||
m_pButtonApply->setEnabled(false);
|
m_pButtonApply->setEnabled(false);
|
||||||
|
m_systemInterruptionCheckTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ public slots:
|
||||||
void logError();
|
void logError();
|
||||||
void updateFound(const QString& version);
|
void updateFound(const QString& version);
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
|
void checkSystemInterruptions();
|
||||||
|
|
||||||
/// @brief Receives the signal that the auto config option has changed
|
/// @brief Receives the signal that the auto config option has changed
|
||||||
void zeroConfToggled();
|
void zeroConfToggled();
|
||||||
|
|
@ -246,6 +247,11 @@ public slots:
|
||||||
QString m_SecureSocketVersion; // brief Contains the version of the Secure Socket currently active
|
QString m_SecureSocketVersion; // brief Contains the version of the Secure Socket currently active
|
||||||
ServerConnection m_serverConnection;
|
ServerConnection m_serverConnection;
|
||||||
ClientConnection m_clientConnection;
|
ClientConnection m_clientConnection;
|
||||||
|
QTimer m_systemInterruptionCheckTimer; // Timer used for sceduling a task for detecting the system
|
||||||
|
// operations preventing the Synergy from working properly
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
bool m_isSecureInputNotificationShown = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
void updateAutoConfigWidgets();
|
void updateAutoConfigWidgets();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,17 @@
|
||||||
|
|
||||||
#define OSXHELPERS__H
|
#define OSXHELPERS__H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
enum class IconsTheme {
|
enum class IconsTheme {
|
||||||
ICONS_DARK,
|
ICONS_DARK,
|
||||||
ICONS_LIGHT,
|
ICONS_LIGHT,
|
||||||
ICONS_TEMPLATE
|
ICONS_TEMPLATE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool isOSXSecureInputEnabled();
|
||||||
|
int getOSXSecureInputEventPID();
|
||||||
|
std::string getOSXProcessName(int pid);
|
||||||
bool isOSXInterfaceStyleDark();
|
bool isOSXInterfaceStyleDark();
|
||||||
IconsTheme getOSXIconsTheme();
|
IconsTheme getOSXIconsTheme();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,77 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <CoreData/CoreData.h>
|
#import <CoreData/CoreData.h>
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <base/Log.h>
|
||||||
|
|
||||||
|
#import <Carbon/Carbon.h>
|
||||||
|
#import <mach/mach_port.h>
|
||||||
|
#import <mach/mach_interface.h>
|
||||||
|
#import <mach/mach_init.h>
|
||||||
|
#import <IOKit/IOMessage.h>
|
||||||
|
#import <IOKit/IOKitLib.h>
|
||||||
|
#import <libproc.h>
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
isOSXSecureInputEnabled()
|
||||||
|
{
|
||||||
|
return IsSecureEventInputEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getOSXSecureInputEventPID()
|
||||||
|
{
|
||||||
|
io_service_t service = MACH_PORT_NULL, service_root = MACH_PORT_NULL;
|
||||||
|
mach_port_t masterPort;
|
||||||
|
|
||||||
|
kern_return_t kr = IOMasterPort( MACH_PORT_NULL, &masterPort );
|
||||||
|
if(kr != KERN_SUCCESS) return 0;
|
||||||
|
|
||||||
|
// IO registry refuses to tap into the root level directly
|
||||||
|
// as a workaround access the parent of the top user level
|
||||||
|
service = IORegistryEntryFromPath( masterPort, kIOServicePlane ":/" );
|
||||||
|
IORegistryEntryGetParentEntry(service, kIOServicePlane, &service_root);
|
||||||
|
|
||||||
|
std::unique_ptr<std::remove_pointer<CFTypeRef>::type, decltype(&CFRelease)> consoleUsers(
|
||||||
|
IORegistryEntrySearchCFProperty(service_root, kIOServicePlane, CFSTR("IOConsoleUsers"), NULL, kIORegistryIterateParents | kIORegistryIterateRecursively),
|
||||||
|
CFRelease
|
||||||
|
);
|
||||||
|
if(!consoleUsers) return 0;
|
||||||
|
|
||||||
|
CFTypeID type = CFGetTypeID(consoleUsers.get());
|
||||||
|
if(type != CFArrayGetTypeID()) return 0;
|
||||||
|
|
||||||
|
CFTypeRef dict = CFArrayGetValueAtIndex((CFArrayRef)consoleUsers.get(), 0);
|
||||||
|
if(!dict) return 0;
|
||||||
|
|
||||||
|
type = CFGetTypeID(dict);
|
||||||
|
if(type != CFDictionaryGetTypeID()) return 0;
|
||||||
|
|
||||||
|
CFTypeRef secureInputPID = nullptr;
|
||||||
|
CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR("kCGSSessionSecureInputPID"), &secureInputPID);
|
||||||
|
|
||||||
|
if(secureInputPID == nullptr) return 0;
|
||||||
|
|
||||||
|
type = CFGetTypeID(secureInputPID);
|
||||||
|
if(type != CFNumberGetTypeID()) return 0;
|
||||||
|
|
||||||
|
auto pidRef = (CFNumberRef)secureInputPID;
|
||||||
|
CFNumberType numberType = CFNumberGetType(pidRef);
|
||||||
|
if(numberType != kCFNumberSInt32Type) return 0;
|
||||||
|
|
||||||
|
int pid;
|
||||||
|
CFNumberGetValue(pidRef, kCFNumberSInt32Type, &pid);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
getOSXProcessName(int pid)
|
||||||
|
{
|
||||||
|
if(!pid) return "";
|
||||||
|
char buf[128];
|
||||||
|
proc_name(pid, buf, sizeof(buf));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
isOSXInterfaceStyleDark()
|
isOSXInterfaceStyleDark()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue