shell bypass 403
#!/usr/bin/perl
package Ocsinventory::Agent;
use strict;
use warnings;
use POSIX ":sys_wait_h";
# THIS IS AN UGLY WORKAROUND FOR
# http://rt.cpan.org/Ticket/Display.html?id=38067
use XML::Simple;
eval {XMLout("<a>b</a>");};
if ($@){
no strict 'refs';
${*{"XML::SAX::"}{HASH}{'parsers'}} = sub {
return [ {
'Features' => {
'http://xml.org/sax/features/namespaces' => '1'
},
'Name' => 'XML::SAX::PurePerl'
}]
};
}
# END OF THE UGLY FIX!
require Exporter;
use Ocsinventory::Logger;
use Ocsinventory::Agent::XML::Inventory;
use Ocsinventory::Agent::XML::Prolog;
use Ocsinventory::Agent::Network;
use Ocsinventory::Agent::Backend;
use Ocsinventory::Agent::AccountConfig;
use Ocsinventory::Agent::AccountInfo;
use Ocsinventory::Agent::Config;
use Ocsinventory::Agent::Hooks;
#use Ocsinventory::Agent::Pid;
use Ocsinventory::Agent::Common;
sub run {
# Load setting from the config file
my $config = new Ocsinventory::Agent::Config;
#$params->{$_} = $config->{$_} foreach (keys %$config);
$ENV{LC_ALL} = 'C'; # Turn off localised output for commands
$ENV{LANG} = 'C'; # Turn off localised output for commands
#####################################
################ MAIN ###############
#####################################
############################
#### CLI parameters ########
############################
$config->loadUserParams();
# I close STDERR to avoid error message during the module execution
# at the beginning I was doing shell redirection:
# my @ret = `cmd 2> /dev/null`;
# but this syntax is not supported on (at least) FreeBSD and Solaris
# c.f: http://www.perlmonks.org/?node_id=571072
#my $tmp;
#open ($tmp, ">&STDERR");
#$params->{"savedstderr"} = $tmp;
#if($params->{debug}) {
# $params->{verbose} = 1;
#} else {
# close(STDERR);
#}
if ($config->{config}{logfile}) {
$config->{config}{logger} = 'File';
}
my $logger = new Ocsinventory::Logger ({
config => $config->{config}
});
my $common = new Ocsinventory::Agent::Common({
logger => $logger,
config => $config->{config},
});
# $< == $REAL_USER_ID
if ( $< ne '0' ) {
$logger->info("You should run this program as super-user.");
}
if (not $config->{config}{scanhomedirs}) {
$logger->debug("--scan-homedirs missing. Don't scan user directories");
}
if ($config->{config}{nosoftware}) {
$config->{config}{nosoftware} = 1
}
# desactivate local mode even if it is set in config file or command line
if (defined($config->{config}{nolocal})) {
undef $config->{config}{'local'};
}
# TODO put that in Ocsinventory::Agent::Config
if (!$config->{config}{'stdout'} && !$config->{config}{'local'} && $config->{config}{server} !~ /^http(|s):\/\//) {
$logger->debug("the --server passed doesn't have a protocol, assume http as default");
$config->{config}{server} = "http://".$config->{config}{server}.'/ocsinventory';
}
######################## Objects initilisation ###############################################################
# The agent can contact different servers. Each server accountconfig is
# stored in a specific file:
if (!recMkdir ($config->{config}{basevardir})) {
if (! -d $ENV{HOME}."/.ocsinventory/var") {
$logger->info("Failed to create ".$config->{config}{basevardir}." directory: $!. ".
"I'm going to use the home directory instead (~/.ocsinventory/var).");
}
$config->{config}{basevardir} = $ENV{HOME}."/.ocsinventory/var";
if (!recMkdir ($config->{config}{basevardir})) {
$logger->error("Failed to create ".$config->{config}{basedir}." directory: $!".
"The HOSTID will not be written on the harddrive. You may have duplicated ".
"entry of this computer in your OCS database");
}
$logger->debug("var files are stored in ".$config->{config}{basevardir});
}
if (defined($config->{config}{server}) && $config->{config}{server}) {
my $dir = $config->{config}{server};
$dir =~ s/\//_/g;
$config->{config}{vardir} = $config->{config}{basevardir}."/".$dir;
if (defined ($config->{config}{local}) && $config->{config}{local}) {
$logger->debug ("--server ignored since you also use --local");
$config->{config}{server} = undef;
}
} elsif (defined($config->{config}{local}) && $config->{config}{local}) {
$config->{config}{vardir} = $config->{config}{basevardir}."/__LOCAL__";
} else {
$logger->error("You must set either --server or --local");
}
if (!recMkdir ($config->{config}{vardir})) {
$logger->error("Failed to create ".$config->{config}{vardir}." directory: $!");
}
if (-d $config->{config}{vardir}) {
$config->{config}{accountconfig} = $config->{config}{vardir}."/ocsinv.conf";
$config->{config}{accountinfofile} = $config->{config}{vardir}."/ocsinv.adm";
$config->{config}{last_statefile} = $config->{config}{vardir}."/last_state";
$config->{config}{next_timefile} = $config->{config}{vardir}."/next_timefile";
}
# Setting SSL CA file path if not set in configuration
unless ($config->{config}{ca}) {
# use server specific cacert.pem if it exists
$config->{config}{ca} = $config->{config}{vardir}.'/cacert.pem';
# if no server specific cacert.pem, look for a bundle in our config dir
unless (-e $config->{config}{vardir}.'/cacert.pem') {
foreach (@{$config->{config}{etcdir}}) {
if (-e $_.'/ocsinventory-agent-cacert.pem') {
$config->{config}{ca} = $_.'/ocsinventory-agent-cacert.pem';
last;
}
}
}
}
################################################################################################################
########################## load CFG files ######################################################################
my $accountconfig = new Ocsinventory::Agent::AccountConfig({
logger => $logger,
config => $config->{config},
});
my $srv = $accountconfig->get('OCSFSERVER');
$config->{config}{server} = $srv if $srv;
$config->{config}{deviceid} = $accountconfig->get('DEVICEID');
# Should I create a new deviceID?
chomp(my $hostname = `uname -n| cut -d . -f 1`);
if ((!$config->{config}{deviceid}) || $config->{config}{deviceid} !~ /\Q$hostname\E-(?:\d{4})(?:-\d{2}){5}/) {
my ($YEAR, $MONTH , $DAY, $HOUR, $MIN, $SEC) = (localtime(time))[5,4,3,2,1,0];
$config->{config}{old_deviceid} = $config->{config}{deviceid};
$config->{config}{deviceid} =sprintf "%s-%02d-%02d-%02d-%02d-%02d-%02d", $hostname, ($YEAR+1900), ($MONTH+1), $DAY, $HOUR, $MIN, $SEC;
$accountconfig->set('DEVICEID',$config->{config}{deviceid});
}
my $accountinfo = new Ocsinventory::Agent::AccountInfo({
logger => $logger,
# TODOparams => $params,
config => $config->{config},
common => $common,
});
# --lazy
if ($config->{config}{lazy}) {
my $nexttime = (stat($config->{config}{next_timefile}))[9];
if ($nexttime && $nexttime > time) {
$logger->info("[Lazy] Must wait until ".localtime($nexttime)." exiting...");
exit 0;
}
}
if ($config->{config}{daemon}) {
$logger->debug("Time to call Proc::Daemon");
eval { require Proc::Daemon; };
if ($@) {
print "Can't load Proc::Daemon. Is the module installed?";
exit 1;
}
Proc::Daemon::Init();
$logger->debug("Daemon started");
if (isAgentAlreadyRunning({
logger => $logger,
})) {
$logger->debug("An agent is already runnnig, exiting...");
exit 1;
}
}
$logger->debug("OCS Agent initialised");
################# Now we can create a context hash #########################################################
my $context = {
installpath => $config->{config}->{vardir},
servername => $config->{config}->{server},
authuser => $config->{config}->{user},
authpwd => $config->{config}->{password},
authrealm => $config->{config}->{realm},
deviceid => $config->{config}->{deviceid},
version => $config->{VERSION},
config => $config->{config},
accountconfig => $accountconfig,
accountinfo => $accountinfo,
logger => $logger,
common => $common,
# OCS_AGENT_CMDL => "TOTO", # TODO cmd line parameter changed with the unified agent
};
################################# HERE WE GO !!! ###################################################
while (1) {
my $exitcode = 0;
my $wait;
my $child;
if ($config->{config}{daemon} || $config->{config}{wait}) {
my $serverdelay;
if(($config->{config}{wait} eq 'server') || ($config->{config}{wait}!~/^\d+$/)){
$serverdelay = $accountconfig->get('PROLOG_FREQ')*3600;
} else {
$serverdelay = $config->{config}{wait};
}
$wait = int rand($serverdelay?$serverdelay:$config->{config}{delaytime});
$logger->info("Going to sleep for $wait second(s)");
sleep ($wait);
}
# Create an hook object to use handlers of modules.
my $hooks = new Ocsinventory::Agent::Hooks($context);
# Using start_handler hook
$hooks->run({name => 'start_handler'});
#################### Local Mode #######################
if ($config->{config}{stdout} || $config->{config}{local}) {
# TODO, avoid to create Backend at two different places
my $backend = new Ocsinventory::Agent::Backend ({
context => $context,
});
my $inventory = new Ocsinventory::Agent::XML::Inventory ({
# TODO, check if the accoun{info,config} are needed in localmode
backend => $backend,
context => $context,
});
# Launching inventory
$inventory->initialise();
# Using inventory_writer hook
$hooks->run({name => 'inventory_handler'}, $inventory);
if ($config->{config}{stdout}) {
$inventory->printXML();
} elsif ($config->{config}{local}) {
$inventory->writeXML();
}
} else {
############ I've to contact the server ########################"
my $network = new Ocsinventory::Agent::Network ({
accountconfig => $accountconfig,
accountinfo => $accountinfo,
logger => $logger,
config => $config->{config},
common => $common,
});
# Adding the network object in $context
$context->{network}= $network;
my $sendInventory = 1;
my $httpresp;
my $prologresp;
if (!$config->{config}{force}) {
my $prolog = new Ocsinventory::Agent::XML::Prolog({
context => $context,
});
# Using prolog_writer hook
$hooks->run({name => 'prolog_writer'}, $prolog);
# Formatting the XML
my $prologXML = $prolog->getContent();
$httpresp = $network->sendXML({message => $prologXML});
$prologresp = $network->getXMLResp($httpresp,'Prolog');
if ($prologresp) {
# Using prolog_reader hook
$hooks->run({name => 'prolog_reader'}, $prologresp->getRawXML());
if (!$prologresp->isInventoryAsked()) {
$sendInventory = 0;
}
} else { # Failed to reach the server
if ($config->{config}{lazy}) {
# To avoid flooding a heavy loaded server
my $previousPrologFreq;
if (!($previousPrologFreq = $accountconfig->get('PROLOG_FREQ'))){
$previousPrologFreq = $config->{config}{delaytime};
$logger->info("No previous PROLOG_FREQ found - using fallback delay(".$config->{config}{delaytime}." seconds)");
} else {
$logger->info("Previous PROLOG_FREQ found ($previousPrologFreq)");
$previousPrologFreq = $previousPrologFreq*3600;
}
my $time = time + $previousPrologFreq;
utime $time,$time,$config->{config}{next_timefile};
}
exit 1 unless $config->{config}{daemon};
$sendInventory = 0;
}
}
if (!$sendInventory) {
$logger->info("Don't send the inventory");
} else { # Send the inventory!
my $backend = new Ocsinventory::Agent::Backend ({
prologresp => $prologresp,
context => $context,
});
my $inventory = new Ocsinventory::Agent::XML::Inventory ({
# TODO, check if the accoun{info,config} are needed in localmode
backend => $backend,
context => $context,
});
# Launching inventory
$inventory->initialise();
# Using inventory_writer hook
$hooks->run({name => 'inventory_handler'}, $inventory);
# Formatting the XML
my $inventoryXML = $inventory->getContent();
# Sending Inventory
$httpresp = $network->sendXML({message => $inventoryXML});
if (my $invresp = $network->getXMLResp($httpresp,'Inventory')) {
$inventory->saveLastState();
} else {
exit (1) unless $config->{config}{daemon};
}
}
}
# Using end_handler_hook
$hooks->run({name => 'end_handler'});
# Avoid zombie process
if ($^O ne 'darwin'){
do {
$child = waitpid(-1, WNOHANG);
} while $child > 0;
} else {
waitpid(-1, 0);
}
exit (0) unless $config->{config}{daemon};
}
}
##########################################
############Functions#####################
##########################################
sub recMkdir {
my $dir = shift;
my @t = split /\//, $dir;
shift @t;
return unless @t;
my $t;
foreach (@t) {
$t .= '/'.$_;
if ((!-d $t) && (!mkdir $t)) {
return;
}
}
1;
}
sub isAgentAlreadyRunning {
my $params = shift;
my $logger = $params->{logger};
# TODO add a workaround if Proc::PID::File is not installed
eval { require Proc::PID::File; };
if(!$@) {
$logger->debug('Proc::PID::File available, checking for pid file');
if (Proc::PID::File->running()) {
$logger->debug('parent process already exists');
return 1;
}
}
return 0;
}
1;