Merge pull request #5602 from Icinga/fix/config-validation-fails-on-windows-with-unprivileged-account-5515

Add windows process elevation and log message if user does not have privileges to read/write files
This commit is contained in:
Michael Friedrich 2017-10-13 16:31:12 +02:00 committed by GitHub
commit 9d68ae9f1f
5 changed files with 139 additions and 7 deletions

View File

@ -40,6 +40,11 @@
# include <sys/types.h>
# include <pwd.h>
# include <grp.h>
#else
# include <windows.h>
# include <Lmcons.h>
# include <Shellapi.h>
# include <tchar.h>
#endif /* _WIN32 */
using namespace icinga;
@ -233,6 +238,74 @@ int Main(void)
return EXIT_FAILURE;
}
#ifdef _WIN32
char username[UNLEN + 1];
DWORD usernameLen = UNLEN + 1;
GetUserName(username, &usernameLen);
std::ifstream userFile;
userFile.open(Application::GetSysconfDir() + "/icinga2/user");
if (userFile && command && !Application::IsProcessElevated()) {
std::string userLine;
if (std::getline(userFile, userLine)) {
userFile.close();
std::vector<std::string> strs;
boost::split(strs, userLine, boost::is_any_of("\\"));
if (username != strs[1] && command->GetImpersonationLevel() == ImpersonationLevel::ImpersonateIcinga
|| command->GetImpersonationLevel() == ImpersonationLevel::ImpersonateRoot) {
TCHAR szPath[MAX_PATH];
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath))) {
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = _T("runas");
sei.lpFile = "cmd.exe";
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI;
sei.nShow = SW_SHOW;
std::stringstream parameters;
parameters << "/C " << "\"" << szPath << "\"" << " ";
for (int i = 1; i < argc; i++) {
if (i != 1)
parameters << " ";
parameters << argv[i];
}
parameters << " & SET exitcode=%errorlevel%";
parameters << " & pause";
parameters << " & EXIT /B %exitcode%";
std::string str = parameters.str();
LPCSTR cstr = str.c_str();
sei.lpParameters = cstr;
if (!ShellExecuteEx(&sei)) {
DWORD dwError = GetLastError();
if (dwError == ERROR_CANCELLED)
Application::Exit(0);
} else {
WaitForSingleObject(sei.hProcess, INFINITE);
DWORD exitCode;
GetExitCodeProcess(sei.hProcess, &exitCode);
CloseHandle(sei.hProcess);
Application::Exit(exitCode);
}
}
}
} else {
userFile.close();
}
}
#endif /* _WIN32 */
#ifndef _WIN32
if (vm.count("color")) {
Console::SetType(std::cout, Console_VT100);

View File

@ -44,6 +44,10 @@
#include <sys/prctl.h>
#endif /* __linux__ */
#ifdef _WIN32
#include <windows.h>
#endif /* _win32 */
using namespace icinga;
REGISTER_TYPE(Application);
@ -780,6 +784,40 @@ BOOL WINAPI Application::CtrlHandler(DWORD type)
SetConsoleCtrlHandler(NULL, FALSE);
return TRUE;
}
bool Application::IsProcessElevated(void) {
BOOL fIsElevated = FALSE;
DWORD dwError = ERROR_SUCCESS;
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
dwError = GetLastError();
else {
TOKEN_ELEVATION elevation;
DWORD dwSize;
if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize))
dwError = GetLastError();
else
fIsElevated = elevation.TokenIsElevated;
}
if (hToken) {
CloseHandle(hToken);
hToken = NULL;
}
if (ERROR_SUCCESS != dwError) {
LPSTR mBuf = NULL;
if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), mBuf, 0, NULL))
BOOST_THROW_EXCEPTION(std::runtime_error("Failed to format error message, last error was: " + dwError));
else
BOOST_THROW_EXCEPTION(std::runtime_error(mBuf));
}
return fIsElevated;
}
#endif /* _WIN32 */
/**

View File

@ -126,6 +126,10 @@ public:
static String GetRunAsGroup(void);
static void DeclareRunAsGroup(const String& group);
#ifdef _WIN32
static bool IsProcessElevated(void);
#endif /* _WIN32 */
static int GetRLimitFiles(void);
static int GetDefaultRLimitFiles(void);
static void DeclareRLimitFiles(int limit);

View File

@ -106,11 +106,16 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs,
if (!configs.empty()) {
for (const String& configPath : configs) {
Expression *expression = ConfigCompiler::CompileFile(configPath, String(), "_etc");
success = ExecuteExpression(expression);
delete expression;
if (!success)
return false;
try {
Expression *expression = ConfigCompiler::CompileFile(configPath, String(), "_etc");
success = ExecuteExpression(expression);
delete expression;
if (!success)
return false;
} catch (const std::exception& ex) {
Log(LogCritical, "cli", "Could not compile config files: " + DiagnosticInformation(ex, false));
Application::Exit(1);
}
}
}
@ -178,7 +183,13 @@ bool DaemonUtility::LoadConfigFiles(const std::vector<std::string>& configs,
}
ConfigCompilerContext::GetInstance()->FinishObjectsFile();
ScriptGlobal::WriteToFile(varsfile);
try {
ScriptGlobal::WriteToFile(varsfile);
} catch (const std::exception& ex) {
Log(LogCritical, "cli", "Could not write vars file: " + DiagnosticInformation(ex, false));
Application::Exit(1);
}
return true;
}

View File

@ -22,6 +22,7 @@
#include "base/json.hpp"
#include "base/netstring.hpp"
#include "base/exception.hpp"
#include "base/application.hpp"
using namespace icinga;
@ -39,7 +40,12 @@ void ConfigCompilerContext::OpenObjectsFile(const String& filename)
m_ObjectsPath = filename;
std::fstream *fp = new std::fstream();
m_ObjectsTempFile = Utility::CreateTempFile(filename + ".XXXXXX", 0600, *fp);
try {
m_ObjectsTempFile = Utility::CreateTempFile(filename + ".XXXXXX", 0600, *fp);
} catch (const std::exception& ex) {
Log(LogCritical, "cli", "Could not create temporary objects file: " + DiagnosticInformation(ex, false));
Application::Exit(1);
}
if (!*fp)
BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + m_ObjectsTempFile + "' file"));