#!/usr/bin/perl -w
# setpassCat.pl
# pod at tail
use strict;
use Net::Telnet::Cisco; # simplify telnet to Cisco devices
use Term::ReadKey; # disable screen echo during password ent
+ry
use Tie::IxHash; # insertion-order hash of passwords+promp
+ts
use Time::localtime; # timestamp of each target device
use vars qw(
$target
$input
$newpass
$newenablepass
$ntc
@cmd_output
);
my %file = (
in => 'spC.in',
sessionlog => 'spC.log',
sessionsummary => 'spC.summ',
NTClog => 'spC.input_log',
);
my %parm = (
pwtimeout => 120, # seconds to wait for password from keybo
+ard
NTCtimeout => 5, # seconds to wait for response from devic
+e
NTCerrmode => 'return', # Net::Telnet::Cisco not die on problem d
+evice
targetcount => 0, # start with device count at zero
);
######################################################################
+#####
umask oct 177; # $input_log file accessible to only this
+ user
PrintBar();
print " Change passwords for multiple Cisco CatOS LAN switches\n";
PrintBar();
######################################################################
+#####
if (-e $file{sessionlog} and -f _) {
unlink ($file{sessionlog})
or die " Error unlinking $file{sessionlog}: $!"
}
open (LOG, "> $file{sessionlog}")
or die " Error opening $file{sessionlog}: $!";
######################################################################
+#####
unless (@ARGV) {
unless (-r $file{in} && -T_) {
Usage();
}
@ARGV = "$file{in}"
or die " Error opening $file{in}: $!";
chomp (@ARGV = <>);
}
######################################################################
+#####
print " Validating target list.\n";
foreach $target (@ARGV) {
chomp $target;
print (" $target\n");
unless ($target =~ (/^(\w|-|\.)+$/)) {
print(
"\n Error reading $file{in}:\n",
" \"$target\" is an improperly formatted device name.\n",
" Only alphanumeric, underscore, dash and dot allowed.\n\
+n",
" Edit $file{in} to remove puctuation,",
" blank lines and/or blank spaces.\n",
);
WrapItUp();
exit;
}
}
print ("\n");
######################################################################
+#####
print " Prompting for existing passwords\n",
" (keystrokes *not* echoed to screen or written to disk)\n";
tie my %prompts, "Tie::IxHash";
%prompts = (
'Enter existing password:' => 'oldpass',
'Enter existing enable password:' => 'olden',
);
tie my %passwds, "Tie::IxHash";
foreach my $prompt (keys %prompts) {
print " $prompt ";
ReadMode('noecho'); # don't echo password to s
+creen
&WaitForPassword();
$passwds{$prompts{$prompt}} = $input;
ReadMode('restore'); # re-activate screen
+ echo
print "\n";
}
print "\n";
my $oldpass = ($passwds{"oldpass"});
my $oldenablepass = ($passwds{"olden"});
######################################################################
+#####
print " Prompting for new passwords\n",
" (keystrokes *not* echoed to screen or written to disk)\n";
tie my %newprompts, "Tie::IxHash";
%newprompts = (
'Enter new password:' => 'newpass',
' Retype to confirm:' => 'confpass',
'Enter new enable password:' => 'newen',
' Retype to confirm: ' => 'confen',
);
tie my %newpasswds, "Tie::IxHash";
foreach my $newprompt(keys %newprompts) {
print " $newprompt ";
ReadMode('noecho'); # don't echo password to scre
+en
&WaitForPassword();
$newpasswds{$newprompts{$newprompt}} = $input;
ReadMode(0); # re-activate screen ec
+ho
print "\n";
}
print "\n";
$newpass = ($newpasswds{"newpass"});
my $confirmpass = ($newpasswds{"confpass"});
$newenablepass = ($newpasswds{"newen"});
my $confirmenablepass = ($newpasswds{"confen"});
unless (
("$newpass" eq "$confirmpass")
and
("$newenablepass" eq "$confirmenablepass")) {
PasswordsMismatch();
}
######################################################################
+#####
PrintLogConsole (" Passwords will be updated on these devices:\n");
for(@ARGV) {
PrintLogConsole (" $_\n");
}
PrintLogConsole ("\n");
Pause ();
PrintBar();
######################################################################
+#####
foreach $target (@ARGV) {
ShowPcTime();
++$parm{targetcount};
if ($ntc = Net::Telnet::Cisco->new (
host => $target,
errmode => $parm{NTCerrmode},
timeout => $parm{NTCtimeout},
input_log => $file{NTClog},
)) {
$ntc -> login('',$oldpass);
PrintLastPrompt(4);
if ($ntc -> enable($oldenablepass)) {
PrintLastPrompt(2);
ChangePass();
} else {
PrintLogConsole ("Error accessing $target\n");
PrintLastPrompt(2);
}
$ntc -> cmd('disa');
print (@cmd_output);
PrintLastPrompt(2);
$ntc -> disable;
PrintLastPrompt(4);
$ntc -> close;
} else {
PrintLogConsole ("Error connecting to $target\n");
}
}
ShowPcTime();
WrapItUp();
ParseLog2Summary();
######################################################################
+#####
# print most recent telnet session prompt to log and console
# param is number of spaces to precede device prompt
sub PrintLastPrompt {
PrintLogConsole (' 'x $_[0], $ntc -> last_prompt, "\n");
}
######################################################################
+#####
# Summarize results
sub ParseLog2Summary {
my $loglines = 0;
my $pwschanged = 0;
my $errors = 0;
open (LOG, "< $file{sessionlog}")
or die " Error opening $file{sessionlog}: $!";
open (SUMMARY, "> $file{sessionsummary}")
or die " Error opening $file{sessionsummary}: $!";
for (<LOG>) {
++$loglines;
++$errors if /^Error /;
++$pwschanged if /^\s+Password changed/;
}
printf " $^O %d:%d:%d %d-%d-%d\n",
localtime -> hour(),
localtime -> min(),
localtime -> sec(),
localtime -> mon()+1,
localtime -> mday(),
localtime -> year()+1900,
;
printf SUMMARY " $^O %d:%d:%d %d-%d-%d\n",
localtime -> hour(),
localtime -> min(),
localtime -> sec(),
localtime -> mon()+1,
localtime -> mday(),
localtime -> year()+1900,
;
PrintSummConsole (" Parsed $loglines line logfile\n\n");
PrintSummConsole (" $parm{targetcount} device(s)\n");
PrintSummConsole (" $pwschanged passwords changed\n");
PrintSummConsole (" $errors error(s)\n\n");
PrintSummConsole (" Review $file{sessionlog} for details.\n");
PrintSummConsole (" Search for \"Error\" if any errors indicated.
+\n");
PrintBar();
close LOG or die " Error closing $file{sessionlog}: $!";
close SUMMARY or die " Error closing $file{sessionsummary}: $!";
}
######################################################################
+#####
# print() instead of cmd() because 'set password' does not return std
+prompt
sub ChangePass {
my $old; my $new;
my @commands = ( 'set password', 'set enablepass' );
foreach my $command (@commands) {
if ($ntc -> print("$command")) {
PrintLogConsole (" $command\n");
}
else { return ("Error sending '$command' command to $target.\n
+"); }
# "$ntc -> getline" must be here to populate @getlines properl
+y
# I don't understand it, but is what made it work |^(
my $getline = $ntc -> getline; # print "\$getline = $getl
+ine\n";
my @getlines = $ntc -> getlines; # print ("\$getlines = $ge
+tlines\n");
foreach my $getlines (@getlines) {
if ($getlines =~ /% Invalid input/) {
PrintLogConsole ("Error running '$command' command at
+$target.\n");
PrintLastPrompt(2);
return(); # skip to next target if c
+ommand fails
}
}
if ($command =~ $commands[0]) { $old = "$oldpass"; $new
+ = "$newpass"; }
if ($command =~ $commands[1]) { $old = "$oldenablepass"; $new
+ = "$newenablepass"; }
# Additional error checking still needed here.
if ($ntc -> waitfor('/Enter old password:/')) {
PrintLastPrompt(2);
$ntc -> print($old);
print (@cmd_output);
}
else {
return PrintLogConsole ("Error getting 'Enter old password' pr
+ompt.\n");
}
if ($ntc ->waitfor('/Enter new password:/')) {
PrintLastPrompt(2);
$ntc -> print($new);
print (@cmd_output);
}
else {
return PrintLogConsole ("Error getting 'Enter new password
+' prompt.\n");
}
if ($ntc ->waitfor('/Retype new password:/')) {
PrintLastPrompt(2);
$ntc -> print($new);
print (@cmd_output);
}
else {
return PrintLogConsole ("Error getting 'Retype new passwor
+d' prompt.\n");
}
if ($ntc ->waitfor('/Password changed./')) {
PrintLastPrompt(2);
}
else {
return PrintLogConsole ("Error getting 'Password changed'
+prompt.\n");
}
}
}
######################################################################
+#####
# Subroutines start here
######################################################################
+#####
sub WaitForPassword {
eval {
local $SIG{ALRM} = sub { die "ALARUM" };
alarm("$parm{pwtimeout}");
chomp($input = <STDIN>);
alarm(0);
};
if ($@ =~ /ALARUM/) {
print "\n\n";
print " Sorry - You waited too long before entering a passwor
+d.\n";
print " Try again, if you want.\n\n";
ReadMode(0); # re-activate screen ec
+ho
exit; # "exit" instead of "die" so no error to conso
+le
}
}
######################################################################
+#####
sub PasswordsMismatch {
print(
" D'oh! Password confirmation(s) didn't match!\n",
" Run this program again if you want, ",
"and try not to fatfinger it next time!\n\n"
);
exit; # "exit" instead of "die" so no error to cons
+ole
}
######################################################################
+#####
sub WrapItUp {
close LOG
or die " Error closing $file{sessionlog}: $!";
PrintBar();
}
######################################################################
+#####
sub Pause {
print " Ctrl+c to abort or <enter> to continue.";
(<STDIN>);
}
######################################################################
+#####
sub ShowPcTime {
printf " $^O %d:%d:%d %d-%d-%d\n",
localtime -> hour(),
localtime -> min(),
localtime -> sec(),
localtime -> mon()+1,
localtime -> mday(),
localtime -> year()+1900,
;
printf LOG " $^O %d:%d:%d %d-%d-%d\n",
localtime -> hour(),
localtime -> min(),
localtime -> sec(),
localtime -> mon()+1,
localtime -> mday(),
localtime -> year()+1900,
;
}
######################################################################
+#####
# print to console *and* logfile
# param is text string to be printed
sub PrintLogConsole {
print @_;
print(LOG @_)
or die " Error printing to $file{sessionlog}: $!";
}
######################################################################
+#####
# print to console *and* logfile
# param is text string to be printed
sub PrintSummConsole {
print @_;
print(SUMMARY @_)
or die " Error printing to $file{sessionsummary}: $!";
}
######################################################################
+#####
sub PrintBar { # Visual divider for ou
+tput
print "\n ", '=' x 54, "\n\n";
}
######################################################################
+#####
sub Usage {
print <<EOF
Oops - you forgot to specify *what* CatOS switches to reset password
+s on!
Usage : setpassCat.pl device1 device2...<enter>
or : setpassCat.pl<enter>
where $file{in} = textfile list of devices address/name,
one per line.
"perldoc setpassCat.pl" for a bit more info.
EOF
;
#my @modules = (
# "Term::ReadKey",
# "Tie::IxHash",
# "Time::localtime",
# "Net::Telnet",
# "Net::Telnet::Cisco",
# );
#foreach my $module (@modules) {
# print ("$module $module::VERSION\n");
# }
print(
" Net::Telnet::Cisco $Net::Telnet::Cisco::VERSION\n",
" Net::Telnet $Net::Telnet::VERSION\n",
" Term::ReadKey $Term::ReadKey::VERSION\n",
" Tie::IxHash $Tie::IxHash::VERSION\n",
" Time::localtime $Time::localtime::VERSION\n",
" Perl $]\n",
" OS $^O\n",
" Program $0\n\n",
);
exit;
}
######################################################################
+#####
=head1 Title
setpassCat.pl
=head1 Description
Automate password resets for multiple Cisco CatOS LAN switches
Runtime is a couple seconds for each CatOS device
CatOS = 2948g plus 4000, 5000 and 6000 family chassis
IOS = 2916, 2924, 3548, and routers
??? = 1900
=head1 Usage
Usage : setpassCat.pl device1 device2...<enter>
or : setpassCat.pl<enter>
where $file{in} = textfile list of devices address/name,
one per line.
=head1 Tested
with:
pass - Debian 2.2r2 "Espy"
Perl 5.00503
Term::ReadKey 2.14
Tie::IxHash 1.21
Time::localtime 1.01
Net::Telnet 3.02
Net::Telnet::Cisco 1.03
fail - Windows 2000pro
SiePerl 5.00503
install fails for
Term::ReadKey
Net::Telnet::Cisco
against: CatOS 4.5(4) on Cisco Catalyst 2948g
5.5(1) 4006
4.5(2) 5500
5.3(5a)CSX 6009
5.5(1) 6509
=head1 Updated
2001-04-30 16:15
Hashamafy file and ntc param scalar variables.
Un-subify to eliminate unecessary global variables.
Mixed-case subroutine names, without ampersan.
Format for 75 chars/line (well, mostly).
2001-04-10 15:00
Insignificant tweaks
2001-03-29
Changed doublequote to singlequote around "=" in PrintBar()
2001-03-28
Removed unecessary quotes around "$_ [0]" in PrintLastPrompt()
Commented function parameters
2001-03-27
Commented reason for extra "$ntc -> getline" in ChangePass()
Removed surrounding quotes from variable definitions
Eliminated unecessary "my $continue" from Pause()
Reduced copy+paste redundancy by adding PrintLastPrompt()
Removed surrounding single quotes from numeric variables
Fixed inconsistant indentation
Tweaked console messages
2001-03-12
Added ParseLog2Summary to count (Password changed|Error)s
and PrintSummConsole for output of above
Simplified output syntax with 'print "=" x 54'
2001-03-09
Simplified ChangePass() with $command =~ $commands[0]
Closed $file{sessionlog} in WrapItUp()
2001-03-08
Added check for 'Invalid input detected'
Moved password timeout to function
Improved error handling if device unreachable
Tweaked console messages
Replaced die"\n" with exit
2001-03-05
Added working timeout for passwords entry
2001-03-04
Initial post to Perl Monks
2001-02-26
Start rewrite of CiscoPassMass using waitfor()
=head1 Comments
The regex to sanity-check infile is very limited.
But it should suffice for most $file{in} problems.
=head1 Todos
Figure out array for installed modules::VERSIONS.
Improve (DNS name|IP address) check for infile sanity-checking.
Net::IPv4Addr - ipv4_checkip
Untaint infile instead of just sanity check.
User.pm to save (log|summary) files to home dir.
=head1 Author
ybiC
=head1 Credits
Thanks to:
Petruchio for mondo suggestions and help
chromatic for $command =~ $commands[0] suggestion on a different post
ar0n for timeout example at [id://30092]
tilly for tips on functions
strredwolf, jcwren, boo_radley, danger, crazyinsomniac, OeufMayo,
azatoth and deprecated for suggestions in CB
Oh yeah, and some guy named vroom {grin}
=cut
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.