mirror of https://github.com/Icinga/icinga2.git
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:
commit
9d68ae9f1f
|
@ -40,6 +40,11 @@
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# include <pwd.h>
|
# include <pwd.h>
|
||||||
# include <grp.h>
|
# include <grp.h>
|
||||||
|
#else
|
||||||
|
# include <windows.h>
|
||||||
|
# include <Lmcons.h>
|
||||||
|
# include <Shellapi.h>
|
||||||
|
# include <tchar.h>
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
@ -233,6 +238,74 @@ int Main(void)
|
||||||
return EXIT_FAILURE;
|
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
|
#ifndef _WIN32
|
||||||
if (vm.count("color")) {
|
if (vm.count("color")) {
|
||||||
Console::SetType(std::cout, Console_VT100);
|
Console::SetType(std::cout, Console_VT100);
|
||||||
|
|
|
@ -44,6 +44,10 @@
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#endif /* __linux__ */
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif /* _win32 */
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
|
||||||
REGISTER_TYPE(Application);
|
REGISTER_TYPE(Application);
|
||||||
|
@ -780,6 +784,40 @@ BOOL WINAPI Application::CtrlHandler(DWORD type)
|
||||||
SetConsoleCtrlHandler(NULL, FALSE);
|
SetConsoleCtrlHandler(NULL, FALSE);
|
||||||
return TRUE;
|
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 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -126,6 +126,10 @@ public:
|
||||||
static String GetRunAsGroup(void);
|
static String GetRunAsGroup(void);
|
||||||
static void DeclareRunAsGroup(const String& group);
|
static void DeclareRunAsGroup(const String& group);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static bool IsProcessElevated(void);
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
static int GetRLimitFiles(void);
|
static int GetRLimitFiles(void);
|
||||||
static int GetDefaultRLimitFiles(void);
|
static int GetDefaultRLimitFiles(void);
|
||||||
static void DeclareRLimitFiles(int limit);
|
static void DeclareRLimitFiles(int limit);
|
||||||
|
|
|
@ -106,11 +106,16 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs,
|
||||||
|
|
||||||
if (!configs.empty()) {
|
if (!configs.empty()) {
|
||||||
for (const String& configPath : configs) {
|
for (const String& configPath : configs) {
|
||||||
Expression *expression = ConfigCompiler::CompileFile(configPath, String(), "_etc");
|
try {
|
||||||
success = ExecuteExpression(expression);
|
Expression *expression = ConfigCompiler::CompileFile(configPath, String(), "_etc");
|
||||||
delete expression;
|
success = ExecuteExpression(expression);
|
||||||
if (!success)
|
delete expression;
|
||||||
return false;
|
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();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "base/json.hpp"
|
#include "base/json.hpp"
|
||||||
#include "base/netstring.hpp"
|
#include "base/netstring.hpp"
|
||||||
#include "base/exception.hpp"
|
#include "base/exception.hpp"
|
||||||
|
#include "base/application.hpp"
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
|
||||||
|
@ -39,7 +40,12 @@ void ConfigCompilerContext::OpenObjectsFile(const String& filename)
|
||||||
m_ObjectsPath = filename;
|
m_ObjectsPath = filename;
|
||||||
|
|
||||||
std::fstream *fp = new std::fstream();
|
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)
|
if (!*fp)
|
||||||
BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + m_ObjectsTempFile + "' file"));
|
BOOST_THROW_EXCEPTION(std::runtime_error("Could not open '" + m_ObjectsTempFile + "' file"));
|
||||||
|
|
Loading…
Reference in New Issue