Sudo.pm: some instance doesn't work - perl

I'm using the Perl module Sudo.pm from CPAN.
Here is the code I'm using:
# In this two variable I'll store the object that runs sudo commands and
# the exit status of the commands
my ($su, $run);
# This functions accept an object returned from sudo->run and will check
# if there were errors running the code.
sub check_status {
# Retrieving the object
my $run = shift;
if (exists($run->{error})) {
print "Failed.\n";
}
else {
print "Done.\n";
}
}
# This first call erase all RSA related files
$su = Sudo->new(
{
sudo => '/usr/bin/sudo',
username => 'root',
pogram => '/usr/bin/rm',
program_args => '-f /tmp/cvmfs_test.key /tmp/cvmfs_test.csr /tmp/cvmfs_test.crt /tmp/whitelist.test.* /tmp/cvmfs_master.key /tmp/cvmfs_master.pub'
}
);
print 'Erasing RSA keys... ';
$run = $su->sudo_run();
check_status($run);
# This instance will erase configuration files created in /etc/cvmfs/config.d
$su = Sudo->new(
{
sudo => '/usr/bin/sudo',
username => 'root',
program => '/usr/bin/rm',
program_args => '-f /etc/cvmfs/config.d/127.0.0.1.conf'
}
);
print 'Erasing configuration files in /etc/cvmfs/config.d... ';
$run = $su->sudo_run();
check_status($run);
# This instance will erase /tmp/cvmfs.faulty
$su = Sudo->new(
{
sudo => '/usr/bin/sudo',
username => 'root',
program => '/usr/bin/rm',
program_args => '-f /tmp/cvmfs.faulty'
}
);
print 'Erasing /tmp/cvmfs.faulty... ';
$run = $su->sudo_run();
print $run->{stdout};
check_status($run);
# This instance will erase all previous extracted repository
$su = Sudo->new(
{
sudo => '/usr/bin/sudo',
username => 'root',
program => '/usr/bin/rm',
program_args => '-fr /tmp/server'
}
);
print 'Erasing /tmp/server directory... ';
$run = $su->sudo_run();
print $run->{stdout};
check_status($run);
# This instance will run 'restarting_services.sh'
$su = Sudo->new(
{
sudo => '/usr/bin/sudo',
username => 'root',
program => 'sh',
program_args => "$Bin/restarting_services.sh"
}
);
print 'Restarting services... ';
$run = $su->sudo_run();
check_status($run);
Is there someone who can explain me why only some of this instance works? To be precise, the first, the second and the fifth instance doesn't work, while the third and the fourth does.
I'm not able to get any output on STDOUT or STDERR and the check_status() function always answer 'Done', for all of them. But this is only because when the command doesn't work, the object is not set at all.
It seems to me I'm using the same syntax for all of them.
Of course, I'm using it with an user that is able to run sudo without password, that's why I didn't added that parameter.
Thank you very much.

There is a typo in your code.
pogram => '/usr/bin/rm',
should be:
program => '/usr/bin/rm',
David W's suggestions for debugging are absolutely spot-on...

Related

incorporating local variables in a global hash in perl

I need to incorporate a generic hash (macro) in multiple user hashes. In actuality these are all specifications written as HoH/HoL in perl.
I would like the 'user' specs to adopt the macro specs with their own modifications. In example below, the variable '$v_Y' needs to have different values in user1 and user2.
What I have below is not exactly code, but an attempt to illustrate the problem. I am unable to have multiple values of $v_Y since macro_spec is already created.
## this is in a package
my $MACRO_SPEC = {
mkeyX => "value_X",
mkeyY => $v_Y,
};
#this is USER1 package,
$v_Y = "U1_VALUE_X";
# use MACRO_SPEC
my $USER1 = (
u1key1 => "u1value1", u1macrokey => $MACRO_SPEC, # need macro to interpolate 'local' $v_Y
);
#this is USER2 package,
$v_Y = "U2_VALUE_X";
# use MACRO_SPEC
my $USER2 = (
u2key1 => "u2value1",
u2macrokey => $MACRO_SPEC, # need macro to interpolate 'local' $v_Y
);
#this is how USER1 should look after the interpolation
my $USER1 = (
u1key1 => "u1value1",
u1macrokey => {
mkeyX => "value_X",
mkeyY => "U1_VALUE_X"
},
);
#this is how USER2 should look after the interpolation
my $USER2 = (
u2key1 => "u2value1",
u1macrokey => {
mkeyX => "value_X",
mkeyY => "U2_VALUE_X"
},
);
Like melpomene suggested, you want $MACRO_SPEC to be a function that can generate something different each time it is called.
package One;
our $v_Y;
my $MACRO_SPEC = sub { +{ mkeyX => "value_X", mkeyY => $v_Y } };
...
package USER1;
$One::v_Y = "U1_VALUE_X";
my $user1 = { # { }, not ( ), to define a hash reference
u1key1 => "u1value1",
u1macrokey => $MACRO_SPEC->(), # $f->() to exec code defined in $f
...
};

How to loop through subarrays of a SOAP::Lite response in Perl?

I have a Perl script that is successfully getting a response from my ShoreTel Phone server. The server provides information on what calls are currently connected for the extension entered. However I am having issues looping through the sub arrays to get more than one response when there are multiple items. In this case I want to get each of the caller IDs that is currently connected.
My SOAP:LITE request is successfully pulling data from the server using the following code:
use strict;
use warnings;
use SOAP::Lite;
use CGI;
use Data::Dumper;
my $myWebService = SOAP::Lite
-> uri('http://www.ShoreTel.com/ProServices/SDK/Web')
-> proxy('http://10.1.##.##:8070/ShoreTelWebSDK/WebService')
-> on_action(sub {sprintf '%s/ShoreTelWebService/%s', $_[0], $_[1]});
my $query = new CGI;
my $ip = $query->remote_host; # IP address of remote party...use later as unique identifier
my $myClientID = $query->param('MyClientID'); # Possible client ID from previous script passed into us.
my $extnNr = $query->param('MyExtn'); # Has to be at least an extension number so we know who to status.
my $url = CGI::url(-path_info=>1); # What is my URL?
# There should be an extension number given, else what would we status.
if (defined($refreshNr) && defined($extnNr) && ($extnNr ne '') && ($refreshNr ne ''))
{
# If there is a client ID defined, use it...otherwise registering and getting a client ID
# is the first thing we need to do when using our web service.
unless (defined($myClientID))
{
# To use our service, we need to register ourselves as a client...use remote IP address
# as a unique name for association to this session.
my $regClientResult = $myWebService->RegisterClient(SOAP::Data->name('clientName' => $ip));
if ($regClientResult->fault)
{
print '<p>FAULT', $myClientID->faultcode, ', ', $myClientID->faultstring;
}
else
{
# Retrieve client ID which we will be using for subsequent communication.
$myClientID = $regClientResult->valueof('//RegisterClientResponse/RegisterClientResult/');
}
}
if (defined($myClientID))
{
# Use our web service to open the line. This is necessary to get a line ID.
# print '<br>Client ID ', $myClientID, ' has been registered.<br>';
my $openResult = $myWebService->OpenLine(SOAP::Data->name('clientHandle' => $myClientID), SOAP::Data->name('lineAddress' => $extnNr));
my $lineID = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineID/');
my $lineType = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineType/');
my $lineName = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineName/');
my $lineState = $openResult->valueof('//OpenLineResponse/OpenLineResult/lineState/');
# Call GetActiveCalls to see if anything is going on with this line.
my $result = $myWebService->GetActiveCalls(SOAP::Data->name('clientHandle' => $myClientID), SOAP::Data->name('lineID' => $lineID));
my $callID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callID/');
if ($callID ne '')
{
# print '<br>Call ID is ', $callID;
my $isExternal = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/isExternal/');
my $isInbound = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/isInbound/');
my $callReason = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callReason/');
my $connectedID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/connectedID/');
my $connectedIDName = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/connectedIDName/');
my $callerID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');
my $callerIDName = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerIDName/');
my $calledID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/calledID/');
my $calledIDName = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/calledIDName/');
my $callState = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callState/');
my $callStateDetail = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callStateDetail/');
# Print call information.
print <<EndOfCallInfo;
HTML CODE
EndOfCallInfo
}
else
{
print <<EndOfCallInfo2;
HTML CODE
EndOfCallInfo2
}
}
}
But I am only able to access the first result in the multidimensional array.
I have tried looping through the results using
for my $t ($result->result({ShoreTelCallStateInfo}{callInfo}')) {
print $t->{callerID} . "\n";}
But I am getting absolutely no results. It appears that the the loop is not even entered.
The following code I have works fine, but only pulls the first caller ID, in this case 1955.
my $callerID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');
What can I do to make my loop work?
So that you can see what I am receiving from the server I have included the response from the SOAP Server using DUMP :
$VAR1 = { 'ShoreTelCallStateInfo' => [
{ 'callStateDetail' => 'Active',
'callState' => 'OnHold',
'callInfo' =>
{ 'callerIDName' => 'Joel LASTNAME',
'callID' => '69105', 'lineID' => '3947',
'connectedIDName' => 'VM-Forward',
'calledID' => '2105',
'callerID' => '1955',
'isInbound' => 'false',
'calledIDName' => 'VM-Forward',
'callReason' => 'None',
'callUniqueID' => '1369702515',
'connectedID' => '2105',
'isExternal' => 'false',
'callGUID' => '{00030000-66C2-537E-3FD8-0010492377D9}'
}
},
{ 'callStateDetail' => 'Active',
'callState' => 'Connected',
'callInfo' =>
{ 'callerIDName' => 'LASTNAME Joel ',
'callID' => '71649',
'lineID' => '3947',
'connectedIDName' => 'LASTNAME Joel ',
'calledID' => '1955',
'callerID' => '+1385#######',
'isInbound' => 'true',
'calledIDName' => 'Joel LASTNAME',
'callReason' => 'None',
'callUniqueID' => '1117287558',
'connectedID' => '+1385#######',
'isExternal' => 'true',
'callGUID' => '{00030000-66C5-537E-3FD8-0010492377D9}'
}
}
]
};
Just a guess...
The following code I have works fine, but only pulls the first caller
ID, in this case 1955.
my $callerID = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');
What can I do to make my loop work?
SOAP::Lite docs say:
valueof()
Returns the value of a (previously) matched node. It accepts a node
path. In this case, it returns the value of matched node, but does not
change the current node. Suitable when you want to match a node and
then navigate through node children:
$som->match('/Envelope/Body/[1]'); # match method
$som->valueof('[1]'); # result
$som->valueof('[2]'); # first out parameter (if present)
The returned value depends on the context. In a scalar context it will
return the first element from matched nodeset. In an array context it
will return all matched elements.
Does this give the behavior you expect? It imposes list context on the valueof method.
for my $callerID ($result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/')) {
...
# do something with each callerID
}
or
my #callerIDs = $result->valueof('//GetActiveCallsResponse/GetActiveCallsResult/ShoreTelCallStateInfo/callInfo/callerID/');

CGI::Session Randomly Logging Users Out

Here is my authentication logic:
sub user_logon {
my ($dbh, $cgi, $cache, $logout) = #_;
#use Log::Log4perl qw(:easy);
#Log::Log4perl->easy_init($DEBUG);
my $session = new CGI::Session("driver:MySQL", $cgi, {Handle=>$dbh});
$session->expires("+3h");
my $auth = new CGI::Session::Auth::DBI({
CGI => $cgi,
Session => $session,
IPAuth => 1,
DBHandle => $dbh,
#Log => 1,
});
if($logout) {
$auth->logout();
}
else {
$auth->authenticate();
if($auth->loggedIn) {
my $user = Cherry::Schema::ResultSet::Employee::get_employee($dbh, $cache, { number => $auth->{userid} });
if (!$user->{active}) {
return { error => $user->{name} . ' is not an active employee.' };
}
$user->{cookie} = $auth->sessionCookie();
return $user;
}
elsif($cgi->param('action') eq 'logon') {
if($cgi->param('log_username') && $cgi->param('log_username')) {
return { error => 'Your username and/or password did not match.' };
}
elsif(!$cgi->param('log_username') || !$cgi->param('log_username')) {
return { error => 'Please enter a username and a password.' };
}
}
else {
return { error => 'You are not logged in' };
}
}
}
sub handle_authentication {
my ($dbh, $cache, $config, $params, $cgi) = #_;
if(($cgi->param('auth') || '') eq 'super_user') { # for automation
return;
}
if(($params->{action} || '') eq 'log_off') {
user_logon($dbh, $cgi, $cache, 1); # 1 means log out
login_form($config, 'Successfully logged out', $params->{login_url}, $params->{title});
}
my $user = user_logon($dbh, $cgi, $cache);
if(exists $user->{error}) {
login_form($config, $user->{error}, $params->{login_url}, $params->{title});
}
elsif($user->{number}) {
return $user;
}
}
Then in my code, every time I print a header, it looks something like this:
my $user = Cherry::Authentication::handle_authentication(
$dbh,
$cache,
\%config,
{
action => $FORM{action},
username => $FORM{log_username},
password => $FORM{log_password},
auth => $FORM{auth}
},
$cgi
);
print header(
-type => 'application/json',
-cookie => $user->{cookie}
);
The problem is that this code seems to work very well about 80% of the time. The other 20%, users are getting kicked out (and not after being stale for 3 hours).
Are there any obvious flaws in this code? Have I left any crucial code out?
If you feel is there not enough information here to give a viable solution, do you have any general suggestions on what can be done to troubleshoot these types of issues?
With this particular problem, there was some code in play that I was unaware of.
$cookie = CGI::Cookie->new(
-name => $session->name, # problem line
-value => $session->id, # problem line
-expires => '+1y',
-path => '/',
-secure => 0,
);
my #header = (
-cookie => $cookie,
-type => 'text/html',
-status => $status
);
print $cgi->header( #header );
The lines with the comments #problem line were assigning a new session even when one already existed.
I installed Fiddler HTTP Debugger on the user's computer that seemed to have the issue the most. Then, once the user was unexpectedly logged out, I reviewed the logs. I was able to find a correlation between the user visiting one url, and the unexpected log out on the next request.

Perl: Unable to check validity of hostname for Net::Appliance::Session

When I tried to pass an invalid hostname, the code will get into an infinite loop.
my $s = Net::Appliance::Session->new({
personality => 'ios',
transport => 'SSH',
host => $ip
});
Is there a way to overcome this bug?
EDIT:
Here's my full code:
I use the subroutine to download the config file of my network device. When I pass in an invalid IP address in download_config, it will get into an infinite loop.
sub download_config
{
my ($ip) = #_;
my $s = Net::Appliance::Session->new({
personality => 'ios',
transport => 'SSH',
host => $ip,
Timeout => 1
});
$s->set_global_log_at('debug'); # maximum debugging
eval {
$s->connect({ username => $username, password => $password });
$s->begin_privileged({ password => $enable_password });
#get hostname to set the file name
$hostname_result = $s->cmd('sh run | inc hostname');
$hostname_result =~ m/hostname (.*)/;
$hostname = $1;
#download the file
my #running_config = $s->cmd('sh run');
#running_config = #running_config[ 2 .. (#running_config -1)];#remove header and footer of the file
open(FH, "> temp/".$hostname.".txt") or die("Cannot open config file : $!");
print FH #running_config;
close FH;
$s->end_privileged;
};
if ($#) {
#when the login details are wrong
print redirect('../../na/unauthorised.html');
}
$s->close;
}
The developer has fixed the bug.
Are you sure?
Are you tried this example code with your host name?
http://cpansearch.perl.org/src/OLIVER/Net-Appliance-Session-3.120560/examples/example-1.pl
If yes, you could create an RT ticket or you could try to contact the author.
I have chacked the code, and did not found anything (too) nasty.
Regards,

Perl SSH connection to execute telnet

I tried the following to access a router via a central admin server as "ssh hop" server
#!/usr/bin/perl -X
use strict;
use Net::OpenSSH;
use Net::Telnet;
my $lhost = "linuxserver";
my $luser = "linuxuser";
my $lpass = "linuxpassword";
my $chost = "routername";
my $cpass = "Routerpassword";
my $prompt = '/(?:Password: |[>])/m';
my #commands = ("show users\r");
my $ssh = Net::OpenSSH->new($lhost,
'user' => $luser,
'password' => $lpass,
'master_opts' => [ '-t' ],
#'async' => 1 # if enabled then password cannot be set here
);
my ($pty, $err, $pid) = $ssh->open2pty("telnet $chost");
my $t = new Net::Telnet(
-telnetmode => 0,
-fhopen => $pty,
-prompt => $prompt,
-cmd_remove_mode => 1,
-output_record_separator => "\r",
#-dump_log => "debug.log",
);
my $end = 0;
while (!$end) {
my ($pre, $post) = $t->waitfor($prompt);
if ($post =~ /Password: /m) {
# send password
$t->print("$cpass");
}
elsif ($post =~ /[>#]/ && #commands) {
my $cmd = shift(#commands);
if ($cmd !~ /[\r\n]/) {
$t->print($cmd);
}
else {
print $t->cmd($cmd);
}
}
else {
$end = 1;
$t->cmd("exit");
}
}
#close $pty;
$t->close();
Unfortunately I always get the following error:
read error: Input/output error at test.pl line 71
Can somebody help me please or is there a better solution only to test if a telnet connection via the "hop" server is possible or not?
The connection looks like:
workstation --ssh-> server --telnet-> router
Thanks in advance.
I think best option is to make an SSH-tunnel to your admin server and use it for telnetting to the router.
Getting Net::Telnet to work over Net::OpenSSH sometimes is not as easy as it should be and it requires some experimentation to get to the right combination of flags and calls that make it work.
For instance, instead of telneting to the target host, use netcat to open a raw connection (or Net::OpenSSH support for TCP forwarding if tunnels are allowed on the proxy).
Expect + Net::OpenSSH may be a better option.