added group and user set[uid|gid] capability at unit definition level

master Examplar-1.9b
Master 2020-06-29 02:22:11 -04:00
parent 3517b9cc11
commit f4a38de0c0
13 changed files with 236 additions and 43 deletions

View File

@ -137,15 +137,22 @@ int main( int argc, char * argv[] )
std::string definitions_file = configuration.get_units_path(); std::string definitions_file = configuration.get_units_path();
std::string plan_file = configuration.get_plan_path(); std::string plan_file = configuration.get_plan_path();
slog.log( E_DEBUG, "* Initialising suite (definition library).");
Suite available_definitions = Suite( L_LEVEL ); Suite available_definitions = Suite( L_LEVEL );
slog.log( E_INFO, "* Loading all actionable units into suite." );
available_definitions.load_units_file( definitions_file ); available_definitions.load_units_file( definitions_file );
slog.log( E_DEBUG, "* Initialising plan." );
Plan plan = Plan( &configuration, L_LEVEL ); Plan plan = Plan( &configuration, L_LEVEL );
slog.log( E_INFO, "* Loading plan outline.");
plan.load_plan_file( plan_file ); plan.load_plan_file( plan_file );
slog.log( E_INFO, "* Loading planned tasks from suite to plan." );
plan.load_definitions( available_definitions ); plan.load_definitions( available_definitions );
slog.log( E_DEBUG, "Ready to execute all tasks in Plan." ); slog.log( E_INFO, "* Ready to execute all actionable tasks in plan." );
try try
{ {
@ -154,6 +161,7 @@ int main( int argc, char * argv[] )
catch ( std::exception& e) catch ( std::exception& e)
{ {
slog.log( E_FATAL, "Caught exception.");
slog.log( E_FATAL, e.what() ); slog.log( E_FATAL, e.what() );
return 1; return 1;
} }

View File

@ -1,15 +1,108 @@
#include "Sproc.h" #include "Sproc.h"
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <sys/wait.h>
#include <wait.h> #include <pwd.h>
#include <grp.h>
#include "../Logger/Logger.h"
int username_to_uid( std::string username, int & uid )
{
// assume failure unless proven otherwise
int r_code = false;
struct passwd * pw;
if ( ( pw = getpwnam( username.c_str() ) ) != NULL )
{
// successful user lookup
r_code = true;
uid = pw->pw_uid;
} else {
// failed lookup, do nothing, assumed failure
}
return r_code;
};
int groupname_to_gid( std::string groupname, int & gid )
{
int r_code = false;
struct group * gp;
if ( ( gp = getgrnam( groupname.c_str() ) ) != NULL )
{
r_code = true;
gid = gp->gr_gid;
} else {
// failed lookup, do nothing, assumed failure
}
return r_code;
}
/// Sproc::execute /// Sproc::execute
/// ///
/// \param input - The commandline input to execute. /// \param input - The commandline input to execute.
/// \return - The return code of the execution of input in the calling shell. /// \return - The return code of the execution of input in the calling shell.
int Sproc::execute(std::string input) { int Sproc::execute(std::string run_as, std::string group, std::string command )
int child_exit_code = -666; {
child_exit_code = system( input.c_str() ); Logger slog = Logger( E_INFO, "_sproc" );
child_exit_code = WEXITSTATUS( child_exit_code );
return child_exit_code; // the run_as_uid to capture the run_as_uid to run as
int run_as_uid;
int run_as_gid;
slog.log( E_DEBUG, "Attempt: Running as user '" + run_as + "'.");
slog.log( E_DEBUG, "Attempt: Running as group '" + group + "'.");
if ( username_to_uid( run_as, run_as_uid ) )
{
slog.log( E_DEBUG, "UID of '" + run_as + "' is '" + std::to_string( run_as_uid ) + "'." );
} else {
slog.log( E_FATAL, "Failed to look up UID for '" + run_as + "'.");
return -404;
}
if ( groupname_to_gid( group, run_as_gid ) )
{
slog.log( E_DEBUG, "GID of '" + group + "' is '" + std::to_string( run_as_gid ) + "'." );
} else {
slog.log( E_FATAL, "Failed to look up DID for '" + group + "'.");
return -404;
}
// if you get this return value, it's an issue with this method and not your
// called executable.
int exit_code_raw = -666;
// fork a process
int pid = fork();
if ( pid == 0 )
{
// child process
if ( seteuid( run_as_uid ) == 0 )
{
slog.log( E_DEBUG, "Successfully set UID to '" + std::to_string(run_as_uid) + "'." );
} else {
slog.log( E_FATAL, "Failed to set UID. Panicking." );
return -401;
}
if ( setegid( run_as_gid ) == 0 )
{
slog.log( E_DEBUG, "Successfully set GID to '" + std::to_string(run_as_gid) + "'." );
} else {
slog.log( E_FATAL, "Failed to set GID. Panicking." );
return -401;
}
exit_code_raw = system( command.c_str() );
exit( WEXITSTATUS( exit_code_raw ) );
} else if ( pid > 0 )
{
// parent process
while ( ( pid = waitpid( pid, &exit_code_raw, 0 ) ) == -1 ) {}
} else {
// fork failed
slog.log( E_FATAL, "Fork Failed");
}
return WEXITSTATUS( exit_code_raw );
} }

View File

@ -23,13 +23,15 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <stdio.h>
#include "../Logger/Logger.h"
// executes a subprocess and captures STDOUT, STDERR, and return code. // executes a subprocess and captures STDOUT, STDERR, and return code.
// should be able to recieve path of binary to be executed as well as any parameters // should be able to recieve path of binary to be executed as well as any parameters
class Sproc { class Sproc {
public: public:
// call the object. returnvalue is enum representing external execution attempt not binary exit code // call the object. returnvalue is enum representing external execution attempt not binary exit code
static int execute( std::string input ); static int execute(std::string run_as, std::string group, std::string command );
}; };
#endif //FTESTS_SPROC_H #endif //FTESTS_SPROC_H

View File

@ -132,7 +132,7 @@ std::string Task::get_name()
void Task::load_definition( Unit selected_unit ) void Task::load_definition( Unit selected_unit )
{ {
this->definition = selected_unit; this->definition = selected_unit;
this->slog.log( E_INFO, "Loaded definition \"" + selected_unit.get_name() + "\" for task \"" + this->get_name() + "\"."); this->slog.log( E_INFO, "Loaded definition \"" + selected_unit.get_name() + "\" as task in configured plan.");
this->defined = true; this->defined = true;
} }
@ -184,7 +184,7 @@ void Task::execute( Conf * configuration )
// END PREWORK // END PREWORK
// get the target execution command // get the target execution command
std::string target_command = configuration->get_execution_context() + + "/" + this->definition.get_target(); std::string target_command = this->definition.get_target();
// check if context override // check if context override
if ( configuration->has_context_override() ) if ( configuration->has_context_override() )
@ -197,10 +197,18 @@ void Task::execute( Conf * configuration )
// a[0] execute target // a[0] execute target
// TODO revise variable sourcing strategy // TODO revise variable sourcing strategy
// ....sourcing on the shell for variables and environment population doesn't have a good smell.
this->slog.log( E_INFO, "Executing target: \"" + target_command + "\"." ); this->slog.log( E_INFO, "Executing target: \"" + target_command + "\"." );
if ( exists( target_command ) )
{
this->slog.log( E_DEBUG, "Executable exists.");
} else {
this->slog.log( E_FATAL, "Executable does not exist." );
throw Task_NotReady();
}
this->slog.log( E_DEBUG, "Vars file: " + configuration->get_env_vars_file() ); this->slog.log( E_DEBUG, "Vars file: " + configuration->get_env_vars_file() );
int return_code = Sproc::execute( ". " + configuration->get_env_vars_file() + " && " + target_command ); int return_code = Sproc::execute( this->definition.get_user(), this->definition.get_group(), ". " + configuration->get_env_vars_file() + " && " + target_command );
// ********************************************** // **********************************************
// d[0] Error Code Check // d[0] Error Code Check
@ -259,7 +267,7 @@ void Task::execute( Conf * configuration )
this->slog.log( E_INFO, "Executing rectification: " + rectifier_command + "." ); this->slog.log( E_INFO, "Executing rectification: " + rectifier_command + "." );
int rectifier_error = Sproc::execute( "source " + configuration->get_env_vars_file() + " && " + rectifier_command ); int rectifier_error = Sproc::execute( this->definition.get_user(), this->definition.get_group(), ". " + configuration->get_env_vars_file() + " && " + rectifier_command );
// ********************************************** // **********************************************
// d[3] Error Code Check for Rectifier // d[3] Error Code Check for Rectifier
@ -300,7 +308,7 @@ void Task::execute( Conf * configuration )
// a[7] Re-execute Target // a[7] Re-execute Target
this->slog.log( E_INFO, "Re-Executing target \"" + this->definition.get_target() + "\"." ); this->slog.log( E_INFO, "Re-Executing target \"" + this->definition.get_target() + "\"." );
int retry_code = Sproc::execute( "source " + configuration->get_env_vars_file() + " && " + target_command ); int retry_code = Sproc::execute( this->definition.get_user(), this->definition.get_group(), ". " + configuration->get_env_vars_file() + " && " + target_command );
// ********************************************** // **********************************************
// d[5] Error Code Check // d[5] Error Code Check

View File

@ -23,6 +23,8 @@
#include <fstream> #include <fstream>
#include <cstdlib> #include <cstdlib>
#include <stdexcept> #include <stdexcept>
#include <pwd.h>
#include <grp.h>
/// Unit_NotPopulated - Meant to be thrown when a Unit type is not populated before being used. /// Unit_NotPopulated - Meant to be thrown when a Unit type is not populated before being used.
/// Signaled by use of the 'populated' boolean member of the Unit class. /// Signaled by use of the 'populated' boolean member of the Unit class.
@ -30,6 +32,11 @@ class Unit_NotPopulated: public std::runtime_error { public:
Unit_NotPopulated(): std::runtime_error("Unit: Attempted to access a member before loading values.") {} Unit_NotPopulated(): std::runtime_error("Unit: Attempted to access a member before loading values.") {}
}; };
/// EnvironmentErrorFatal - Meant to be thrown when the environment is too broken for Examplar to do its job.
class EnvironmentErrorFatal: public std::runtime_error { public:
EnvironmentErrorFatal(): std::runtime_error("Unit: Environment is too broken to continue.") {}
};
/// Unit_DataStructureException - Meant to be thrown when a Unit type is accessing a member that does not exist. /// Unit_DataStructureException - Meant to be thrown when a Unit type is accessing a member that does not exist.
class Unit_DataStructureException: public std::runtime_error { public: class Unit_DataStructureException: public std::runtime_error { public:
// TODO rework this to accept the key name being fetched // TODO rework this to accept the key name being fetched
@ -83,6 +90,44 @@ int Unit::load_root(Json::Value loader_root)
if ( loader_root.isMember("rectify") ) if ( loader_root.isMember("rectify") )
{ this->rectify = loader_root.get("rectify", errmsg).asBool(); } else throw Unit_DataStructureException(); { this->rectify = loader_root.get("rectify", errmsg).asBool(); } else throw Unit_DataStructureException();
// TODO functionize this
char * lgn;
std::string errmsg_user;
// if no user field is specified then default to the currently executing user
if ( ( lgn = getlogin() ) == NULL )
{
throw EnvironmentErrorFatal();
} else {
errmsg_user = lgn;
}
// -TODO
if ( loader_root.isMember( "user" ) )
{ this->user = loader_root.get( "user", errmsg_user ).asString(); } else this->user = lgn;
// TODO functionalize this
// get the current context gid as a backup value
int gid = getgid();
// declare the grp object to pull the name from once populated
struct group * grp;
// storage for backup value once retrieved
std::string errmsg_group;
// get the backup value and store it to errmsg_group
if ( ( grp = getgrgid( gid ) ) == NULL )
{
throw EnvironmentErrorFatal();
} else {
errmsg_group = grp->gr_name;
}
if ( loader_root.isMember( "group" ) )
{ this->group = loader_root.get( "group", errmsg_group ).asString(); } else this->group = grp->gr_name;
this->populated = true; this->populated = true;
return 0; return 0;
@ -164,4 +209,21 @@ bool Unit::get_rectify()
{ {
if ( ! this->populated ) { throw Unit_NotPopulated(); } if ( ! this->populated ) { throw Unit_NotPopulated(); }
return this->rectify; return this->rectify;
}
/// Unit::get_user - retrieves the user context for the unit.
///
/// \return the string value of the user name.
std::string Unit::get_user()
{
if ( ! this->populated ) { throw Unit_NotPopulated(); }
return this->user;
}
/// Unit::get_group - retrieves the group context for the unit.
///
/// \return the string value of the group name.
std::string Unit::get_group()
{
if ( ! this->populated ) { throw Unit_NotPopulated(); }
return this->group;
} }

View File

@ -59,6 +59,14 @@ private:
// if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required // if rectifier exits on non-zero return code, it should be trigger the behaviour indicated by required
bool rectify; bool rectify;
// user to run process as.
// not intended for protected accounts, handle your own security
std::string user;
// group to run process as.
// not intended for protected accounts, handle your own security
std::string group;
public: public:
Unit( int LOG_LEVEL ); Unit( int LOG_LEVEL );
@ -76,6 +84,8 @@ public:
bool get_active(); bool get_active();
bool get_required(); bool get_required();
bool get_rectify(); bool get_rectify();
std::string get_user();
std::string get_group();
private: private:
int LOG_LEVEL; int LOG_LEVEL;

View File

@ -0,0 +1,2 @@
echo "dependent test"
exit $?

View File

@ -0,0 +1,11 @@
whoami
id
touch /home/bagira/testfile
stat /home/bagira/testfile
#dialog --stdout --title "Interact with me!" \
# --backtitle "This is user interaction." \
# --yesno "Yes: pass, No: fail" 7 60
exit $?

View File

@ -0,0 +1,2 @@
echo "independent test 2 output"
exit $?

View File

@ -1,8 +1,8 @@
{ {
"execution_context_override": true, "execution_context_override": true,
"execution_context": "/home/bagira/development/internal/examplar/test", "execution_context": "/home/bagira/development/internal/examplar/test",
"units_path": "units/all_test.units", "units_path": "units/",
"plan_path": "plans/test.plan", "plan_path": "plans/atomic.plan",
"config_version": "3", "config_version": "3",
"env_vars_file": "examplar.variables" "env_vars_file": "examplar.variables"
} }

5
test/plans/atomic.plan Normal file
View File

@ -0,0 +1,5 @@
{
"plan": [
{ "name": "independent test 1", "dependencies": [ null ] }
]
}

View File

@ -2,35 +2,23 @@
"units": [ "units": [
{ {
"name": "independent test 1", "name": "independent test 1",
"target": "/usr/bin/true", "target": "components/independent_test_1.bash",
"rectifier": "/usr/bin/true", "rectifier": "",
"active": true, "active": true,
"required": true, "required": true,
"rectify": true "user": "root",
}, "group": "root",
{
"name": "independent test 2",
"target": "/usr/bin/true",
"rectifier": "/usr/bin/true",
"active": true,
"required": false,
"rectify": false "rectify": false
}, },
{ {
"name": "A DEFINITION THAT IS NOT USED", "name": "independent test 2",
"target": "/usr/bin/dialog --yesno test 50 50", "target": "components/independent_test_2.bash",
"rectifier": "/usr/bin/false", "rectifier": "",
"active": false,
"required": false,
"rectify": true
},
{
"name": "dependent test",
"target": "/usr/bin/false",
"rectifier": "/usr/bin/true",
"active": true, "active": true,
"required": true, "required": false,
"rectify": true "user": "bagira",
"group": "bagira",
"rectify": false
} }
] ]
} }

View File

@ -4,17 +4,19 @@
"name": "A DEFINITION THAT IS NOT USED", "name": "A DEFINITION THAT IS NOT USED",
"target": "/usr/bin/dialog --yesno test 50 50", "target": "/usr/bin/dialog --yesno test 50 50",
"rectifier": "/usr/bin/false", "rectifier": "/usr/bin/false",
"active": true, "active": false,
"required": true, "required": true,
"rectify": true "rectify": true,
"user": "root"
}, },
{ {
"name": "dependent test", "name": "dependent test",
"target": "/usr/bin/false", "target": "/usr/bin/false",
"rectifier": "/usr/bin/true", "rectifier": "/usr/bin/true",
"active": true, "active": false,
"required": true, "required": true,
"rectify": true "rectify": true,
"user": "root"
} }
] ]
} }