Auto elevate for Windows UAC and screen lock #4130

This commit is contained in:
Xinyu Hou 2014-10-27 16:39:18 +00:00
parent 4d3fd14ada
commit d2191b6b93
12 changed files with 352 additions and 54 deletions

View File

@ -171,3 +171,21 @@ CMSWindowsSession::nextProcessEntry(HANDLE snapshot, LPPROCESSENTRY32 entry)
return gotEntry;
}
CString
CMSWindowsSession::getActiveDesktopName()
{
CString result;
HDESK hd = OpenInputDesktop(0, TRUE, GENERIC_READ);
if (hd != NULL) {
DWORD size;
GetUserObjectInformation(hd, UOI_NAME, NULL, 0, &size);
TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR));
GetUserObjectInformation(hd, UOI_NAME, name, size, &size);
result = name;
CloseDesktop(hd);
}
return result;
}

View File

@ -17,6 +17,8 @@
#pragma once
#include "base/String.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Tlhelp32.h>
@ -40,6 +42,8 @@ public:
void updateActiveSession();
CString getActiveDesktopName();
private:
BOOL nextProcessEntry(HANDLE snapshot, LPPROCESSENTRY32 entry);

View File

@ -43,6 +43,8 @@ enum {
typedef VOID (WINAPI *SendSas)(BOOL asUser);
const char g_activeDesktop[] = {"activeDesktop:"};
CMSWindowsWatchdog::CMSWindowsWatchdog(
bool autoDetectCommand,
CIpcServer& ipcServer,
@ -58,12 +60,23 @@ CMSWindowsWatchdog::CMSWindowsWatchdog(
m_elevateProcess(false),
m_processFailures(0),
m_processRunning(false),
m_fileLogOutputter(NULL)
m_fileLogOutputter(NULL),
m_autoElevated(false),
m_ready(false)
{
m_mutex = ARCH->newMutex();
m_condVar = ARCH->newCondVar();
}
CMSWindowsWatchdog::~CMSWindowsWatchdog()
{
if (m_condVar != NULL) {
ARCH->closeCondVar(m_condVar);
}
if (m_mutex != NULL) {
ARCH->closeMutex(m_mutex);
}
}
void
@ -126,7 +139,9 @@ CMSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security)
// elevate for the uac dialog (consent.exe) but this would be pointless,
// since synergy would re-launch as non-elevated after the desk switch,
// and so would be unusable with the new elevated process taking focus.
if (m_elevateProcess || m_session.isProcessInSession("logonui.exe", NULL)) {
if (m_elevateProcess
|| m_autoElevated
|| m_session.isProcessInSession("logonui.exe", NULL)) {
LOG((CLOG_DEBUG "getting elevated token, %s",
(m_elevateProcess ? "elevation required" : "at login screen")));
@ -273,7 +288,11 @@ CMSWindowsWatchdog::startProcess()
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
getActiveDesktop(&sa);
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
HANDLE userToken = getUserToken(&sa);
m_autoElevated = false;
// patch by Jack Zhou and Henry Tung
// set UIAccess to fix Windows 8 GUI interaction
@ -281,6 +300,33 @@ CMSWindowsWatchdog::startProcess()
DWORD uiAccess = 1;
SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD));
BOOL createRet = doStartProcess(m_command, userToken, &sa);
if (!createRet) {
LOG((CLOG_ERR "could not launch"));
DWORD exitCode = 0;
GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
LOG((CLOG_ERR "exit code: %d", exitCode));
throw XArch(new XArchEvalWindows);
}
else {
// wait for program to fail.
ARCH->sleep(1);
if (!isProcessActive()) {
throw XMSWindowsWatchdogError("process immediately stopped");
}
m_processRunning = true;
m_processFailures = 0;
LOG((CLOG_DEBUG "started process, session=%i, command=%s",
m_session.getActiveSessionId(), m_command.c_str()));
}
}
BOOL
CMSWindowsWatchdog::doStartProcess(CString& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
{
// clear, as we're reusing process info struct
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
@ -307,30 +353,14 @@ CMSWindowsWatchdog::startProcess()
// re-launch in current active user session
LOG((CLOG_INFO "starting new process"));
BOOL createRet = CreateProcessAsUser(
userToken, NULL, LPSTR(m_command.c_str()),
&sa, NULL, TRUE, creationFlags,
userToken, NULL, LPSTR(command.c_str()),
sa, NULL, TRUE, creationFlags,
environment, NULL, &si, &m_processInfo);
DestroyEnvironmentBlock(environment);
CloseHandle(userToken);
if (!createRet) {
LOG((CLOG_ERR "could not launch"));
throw XArch(new XArchEvalWindows);
}
else {
// wait for program to fail.
ARCH->sleep(1);
if (!isProcessActive()) {
throw XMSWindowsWatchdogError("process immediately stopped");
}
m_processRunning = true;
m_processFailures = 0;
LOG((CLOG_DEBUG "started process, session=%i, command=%s",
m_session.getActiveSessionId(), m_command.c_str()));
}
return createRet;
}
void
@ -388,6 +418,8 @@ CMSWindowsWatchdog::outputLoop(void*)
else {
buffer[bytesRead] = '\0';
testOutput(buffer);
// send process output over IPC to GUI, and force it to be sent
// which bypasses the ipc logging anti-recursion mechanism.
m_ipcLogOutputter.write(kINFO, buffer, true);
@ -492,3 +524,57 @@ CMSWindowsWatchdog::shutdownExistingProcesses()
CloseHandle(snapshot);
m_processRunning = false;
}
void
CMSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security)
{
CString installedDir = ARCH->getInstalledDirectory();
if (!installedDir.empty()) {
CString syntoolCommand;
syntoolCommand.append("\"").append(installedDir).append("\\").append("syntool").append("\"");
syntoolCommand.append(" --get-active-desktop");
m_session.updateActiveSession();
bool elevateProcess = m_elevateProcess;
m_elevateProcess = true;
HANDLE userToken = getUserToken(security);
m_elevateProcess = elevateProcess;
BOOL createRet = doStartProcess(syntoolCommand, userToken, security);
if (!createRet) {
DWORD rc = GetLastError();
RevertToSelf();
}
else {
LOG((CLOG_DEBUG "launched syntool to check active desktop"));
}
ARCH->lockMutex(m_mutex);
while (!m_ready) {
ARCH->waitCondVar(m_condVar, m_mutex, 1.0);
}
m_ready = false;
ARCH->unlockMutex(m_mutex);
}
}
void
CMSWindowsWatchdog::testOutput(CString buffer)
{
// HACK: check standard output seems hacky.
size_t i = buffer.find(g_activeDesktop);
if (i != CString::npos) {
size_t s = sizeof(g_activeDesktop);
CString defaultDesktop("Default");
CString sub = buffer.substr(s - 1, defaultDesktop.size());
if (sub != defaultDesktop) {
m_autoElevated = true;
}
ARCH->lockMutex(m_mutex);
m_ready = true;
ARCH->broadcastCondVar(m_condVar);
ARCH->unlockMutex(m_mutex);
}
}

View File

@ -20,6 +20,7 @@
#include "platform/MSWindowsSession.h"
#include "synergy/XSynergy.h"
#include "arch/IArchMultithread.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
@ -54,7 +55,10 @@ private:
HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security);
HANDLE getUserToken(LPSECURITY_ATTRIBUTES security);
void startProcess();
BOOL doStartProcess(CString& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa);
void sendSas();
void getActiveDesktop(LPSECURITY_ATTRIBUTES security);
void testOutput(CString buffer);
private:
CThread* m_thread;
@ -73,6 +77,10 @@ private:
int m_processFailures;
bool m_processRunning;
CFileLogOutputter* m_fileLogOutputter;
bool m_autoElevated;
CArchMutex m_mutex;
CArchCond m_condVar;
bool m_ready;
};
//! Relauncher error

View File

@ -244,3 +244,80 @@ CApp::runEventsLoop(void*)
#endif
}
//
// CMinimalApp
//
CMinimalApp::CMinimalApp() :
CApp(NULL, NULL, new CArgsBase())
{
setEvents(m_events);
}
CMinimalApp::~CMinimalApp()
{
}
int
CMinimalApp::standardStartup(int argc, char** argv)
{
return 0;
}
int
CMinimalApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
{
return 0;
}
void
CMinimalApp::startNode()
{
}
int
CMinimalApp::mainLoop()
{
return 0;
}
int
CMinimalApp::foregroundStartup(int argc, char** argv)
{
return 0;
}
CScreen*
CMinimalApp::createScreen()
{
return NULL;
}
void
CMinimalApp::loadConfig()
{
}
bool
CMinimalApp::loadConfig(const CString& pathname)
{
return false;
}
const char*
CMinimalApp::daemonInfo() const
{
return "";
}
const char*
CMinimalApp::daemonName() const
{
return "";
}
void
CMinimalApp::parseArgs(int argc, const char* const* argv)
{
}

View File

@ -18,10 +18,12 @@
#pragma once
#include "common/common.h"
#include "base/String.h"
#include "synergy/IApp.h"
#include "ipc/IpcClient.h"
#include "synergy/IApp.h"
#include "base/String.h"
#include "base/Log.h"
#include "base/EventQueue.h"
#include "common/common.h"
#if SYSAPI_WIN32
#include "synergy/win32/AppUtilWindows.h"
@ -96,6 +98,8 @@ public:
void setSocketMultiplexer(CSocketMultiplexer* sm) { m_socketMultiplexer = sm; }
CSocketMultiplexer* getSocketMultiplexer() const { return m_socketMultiplexer; }
void setEvents(CEventQueue& events) { m_events = &events; }
private:
void handleIpcMessage(const CEvent&, void*);
@ -118,6 +122,30 @@ private:
CSocketMultiplexer* m_socketMultiplexer;
};
class CMinimalApp : public CApp {
public:
CMinimalApp();
virtual ~CMinimalApp();
// IApp overrides
virtual int standardStartup(int argc, char** argv);
virtual int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup);
virtual void startNode();
virtual int mainLoop();
virtual int foregroundStartup(int argc, char** argv);
virtual CScreen* createScreen();
virtual void loadConfig();
virtual bool loadConfig(const CString& pathname);
virtual const char* daemonInfo() const;
virtual const char* daemonName() const;
virtual void parseArgs(int argc, const char* const* argv);
private:
CArch m_arch;
CLog m_log;
CEventQueue m_events;
};
#if WINAPI_MSWINDOWS
#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
#else

View File

@ -20,6 +20,7 @@
#include "synergy/App.h"
#include "synergy/ServerArgs.h"
#include "synergy/ClientArgs.h"
#include "synergy/ToolArgs.h"
#include "synergy/ArgsBase.h"
#include "base/Log.h"
@ -155,6 +156,23 @@ CArgParser::parsePlatformArg(CArgsBase& argsBase, const int& argc, const char* c
#endif
}
bool
CArgParser::parseToolArgs(CToolArgs& args, int argc, const char* const* argv)
{
for (int i = 1; i < argc; ++i) {
if (isArg(i, argc, argv, NULL, "--get-active-desktop", 0)) {
args.m_printActiveDesktopName = true;
return true;
}
else {
return false;
}
}
return false;
}
bool
CArgParser::parseGenericArgs(int argc, const char* const* argv, int& i)
{

View File

@ -34,6 +34,7 @@ public:
bool parseServerArgs(CServerArgs& args, int argc, const char* const* argv);
bool parseClientArgs(CClientArgs& args, int argc, const char* const* argv);
bool parsePlatformArg(CArgsBase& argsBase, const int& argc, const char* const* argv, int& i);
bool parseToolArgs(CToolArgs& args, int argc, const char* const* argv);
bool parseGenericArgs(int argc, const char* const* argv, int& i);
void setArgsBase(CArgsBase& argsBase) { m_argsBase = &argsBase; }

View File

@ -16,14 +16,18 @@
*/
#include "synergy/ToolApp.h"
#include "synergy/ArgParser.h"
#include "arch/Arch.h"
#include "base/Log.h"
#include "base/String.h"
#include <iostream>
#include <sstream>
//#define PREMIUM_AUTH_URL "http://localhost/synergy/premium/json/auth/"
#define PREMIUM_AUTH_URL "https://synergy-project.org/premium/json/auth/"
#if SYSAPI_WIN32
#include "platform/MSWindowsSession.h"
#endif
enum {
kErrorOk,
@ -41,43 +45,43 @@ CToolApp::run(int argc, char** argv)
}
try {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--premium-auth") == 0) {
premiumAuth();
return kErrorOk;
CArgParser argParser(this);
bool result = argParser.parseToolArgs(m_args, argc, argv);
if (!result) {
m_bye(kExitArgs);
}
if (m_args.m_printActiveDesktopName) {
#if SYSAPI_WIN32
CMSWindowsSession session;
CString name = session.getActiveDesktopName();
if (name.empty()) {
LOG((CLOG_CRIT "failed to get active desktop name"));
return kExitFailed;
}
else {
std::cerr << "unknown arg: " << argv[i] << std::endl;
return kErrorArgs;
std::cout << "activeDesktop:" << name.c_str() << std::endl;
}
#endif
}
else {
throw XSynergy("Nothing to do");
}
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
return kErrorException;
LOG((CLOG_CRIT "An error occurred: %s\n", e.what()));
return kExitFailed;
}
catch (...) {
std::cerr << "unknown error" << std::endl;
return kErrorUnknown;
LOG((CLOG_CRIT "An unknown error occurred.\n"));
return kExitFailed;
}
return kErrorOk;
}
void
CToolApp::premiumAuth()
CToolApp::help()
{
CString credentials;
std::cin >> credentials;
size_t separator = credentials.find(':');
CString email = credentials.substr(0, separator);
CString password = credentials.substr(separator + 1, credentials.length());
std::stringstream ss;
ss << PREMIUM_AUTH_URL;
ss << "?email=" << ARCH->internet().urlEncode(email);
ss << "&password=" << password;
std::cout << ARCH->internet().get(ss.str()) << std::endl;
}

View File

@ -17,12 +17,15 @@
#pragma once
#include "synergy/App.h"
#include "synergy/ToolArgs.h"
#include "common/basic_types.h"
class CToolApp {
class CToolApp : public CMinimalApp
{
public:
UInt32 run(int argc, char** argv);
void help();
private:
void premiumAuth();
CToolArgs m_args;
};

View File

@ -0,0 +1,23 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2014 Synergy Si, Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "synergy/ToolArgs.h"
CToolArgs::CToolArgs() :
m_printActiveDesktopName(false)
{
}

View File

@ -0,0 +1,28 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2014 Synergy Si, Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "base/String.h"
class CToolArgs {
public:
CToolArgs();
public:
bool m_printActiveDesktopName;
};