Implement support for running specific CLI commands as root

fixes #7380
This commit is contained in:
Gunnar Beutner 2014-10-24 15:29:46 +02:00
parent d3c9e052e9
commit 80a3298b5e
11 changed files with 97 additions and 53 deletions

View File

@ -333,66 +333,73 @@ int Main(void)
rc = 0;
} else if (command) {
#ifndef _WIN32
String group = Application::GetRunAsGroup();
errno = 0;
struct group *gr = getgrnam(group.CStr());
if (!gr) {
if (errno == 0) {
Log(LogCritical, "cli")
<< "Invalid group specified: " << group;
return EXIT_FAILURE;
} else {
Log(LogCritical, "cli")
<< "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
if (command->GetImpersonationLevel() == ImpersonateRoot) {
if (getuid() != 0) {
Log(LogCritical, "cli", "This command must be run as root.");
return 0;
}
}
} else if (command && command->GetImpersonationLevel() == ImpersonateIcinga) {
String group = Application::GetRunAsGroup();
if (getgid() != gr->gr_gid) {
if (!vm.count("reload-internal") && setgroups(0, NULL) < 0) {
Log(LogCritical, "cli")
<< "setgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
errno = 0;
struct group *gr = getgrnam(group.CStr());
if (!gr) {
if (errno == 0) {
Log(LogCritical, "cli")
<< "Invalid group specified: " << group;
return EXIT_FAILURE;
} else {
Log(LogCritical, "cli")
<< "getgrnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
}
if (setgid(gr->gr_gid) < 0) {
Log(LogCritical, "cli")
<< "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
}
if (getgid() != gr->gr_gid) {
if (!vm.count("reload-internal") && setgroups(0, NULL) < 0) {
Log(LogCritical, "cli")
<< "setgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
String user = Application::GetRunAsUser();
errno = 0;
struct passwd *pw = getpwnam(user.CStr());
if (!pw) {
if (errno == 0) {
Log(LogCritical, "cli")
<< "Invalid user specified: " << user;
return EXIT_FAILURE;
} else {
Log(LogCritical, "cli")
<< "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
}
// also activate the additional groups the configured user is member of
if (getuid() != pw->pw_uid) {
if (!vm.count("reload-internal") && initgroups(user.CStr(), pw->pw_gid) < 0) {
Log(LogCritical, "cli")
<< "initgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
if (setgid(gr->gr_gid) < 0) {
Log(LogCritical, "cli")
<< "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
}
if (setuid(pw->pw_uid) < 0) {
Log(LogCritical, "cli")
<< "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
String user = Application::GetRunAsUser();
errno = 0;
struct passwd *pw = getpwnam(user.CStr());
if (!pw) {
if (errno == 0) {
Log(LogCritical, "cli")
<< "Invalid user specified: " << user;
return EXIT_FAILURE;
} else {
Log(LogCritical, "cli")
<< "getpwnam() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
}
// also activate the additional groups the configured user is member of
if (getuid() != pw->pw_uid) {
if (!vm.count("reload-internal") && initgroups(user.CStr(), pw->pw_gid) < 0) {
Log(LogCritical, "cli")
<< "initgroups() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
if (setuid(pw->pw_uid) < 0) {
Log(LogCritical, "cli")
<< "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
return EXIT_FAILURE;
}
}
}
#endif /* _WIN32 */

View File

@ -77,6 +77,11 @@ std::vector<String> AgentSetupCommand::GetArgumentSuggestions(const String& argu
return CLICommand::GetArgumentSuggestions(argument, word);
}
ImpersonationLevel AgentSetupCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
/**
* The entry point for the "agent setup" CLI command.
*

View File

@ -40,6 +40,7 @@ public:
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const;
virtual std::vector<String> GetArgumentSuggestions(const String& argument, const String& word) const;
virtual ImpersonationLevel GetImpersonationLevel(void) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
private:

View File

@ -159,6 +159,11 @@ void CLICommand::InitParameters(boost::program_options::options_description& vis
boost::program_options::options_description& hiddenDesc) const
{ }
ImpersonationLevel CLICommand::GetImpersonationLevel(void) const
{
return ImpersonateIcinga;
}
bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& visibleDesc,
po::options_description& hiddenDesc,
po::positional_options_description& positionalDesc,

View File

@ -32,6 +32,13 @@ namespace icinga
std::vector<String> I2_CLI_API GetBashCompletionSuggestions(const String& type, const String& word);
std::vector<String> I2_CLI_API GetFieldCompletionSuggestions(const Type *type, const String& word);
enum ImpersonationLevel
{
ImpersonateNone,
ImpersonateRoot,
ImpersonateIcinga
};
/**
* A CLI command.
*
@ -50,6 +57,7 @@ public:
virtual int GetMaxArguments(void) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const;
virtual ImpersonationLevel GetImpersonationLevel(void) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const = 0;
virtual std::vector<String> GetArgumentSuggestions(const String& argument, const String& word) const;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const;

View File

@ -51,6 +51,11 @@ int FeatureDisableCommand::GetMaxArguments(void) const
return -1;
}
ImpersonationLevel FeatureDisableCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
/**
* The entry point for the "feature disable" CLI command.
*

View File

@ -40,6 +40,7 @@ public:
virtual int GetMinArguments(void) const;
virtual int GetMaxArguments(void) const;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const;
virtual ImpersonationLevel GetImpersonationLevel(void) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
};

View File

@ -51,6 +51,11 @@ int FeatureEnableCommand::GetMaxArguments(void) const
return -1;
}
ImpersonationLevel FeatureEnableCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
/**
* The entry point for the "feature enable" CLI command.
*

View File

@ -40,6 +40,7 @@ public:
virtual int GetMinArguments(void) const;
virtual int GetMaxArguments(void) const;
virtual std::vector<String> GetPositionalSuggestions(const String& word) const;
virtual ImpersonationLevel GetImpersonationLevel(void) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
};

View File

@ -58,6 +58,11 @@ void RepositoryCommitCommand::InitParameters(boost::program_options::options_des
("simulate", "Simulate to-be-committed changes");
}
ImpersonationLevel RepositoryCommitCommand::GetImpersonationLevel(void) const
{
return ImpersonateRoot;
}
/**
* The entry point for the "repository commit" CLI command.
*

View File

@ -42,6 +42,7 @@ public:
virtual String GetShortDescription(void) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const;
virtual ImpersonationLevel GetImpersonationLevel(void) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
};