Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

(code) Poor Man's TACACS - automate CatOS and IOS password resets with Net::Telnet::Cisco and Net::SNMP

by ybiC (Prior)
on Nov 06, 2001 at 03:50 UTC ( [id://123464]=sourcecode: print w/replies, xml ) Need Help??
Category: Networking Code
Author/Contact Info ybiC
Description: Automate password updates for any number of Cisco routers and Catalyst switches.   Uses Net::SNMP to detect device+code types, and Net::Telnet::Cisco to make the changes.   Old, new, and confirm password entries blanked by Term::Readkey.   Command line switches facilitated by Getopt::Long.

Is a substantial rewrite and consolidation of the now deprecated "(code)) Cisco Pass Mass - IOS (deprecated by node 123464)" and "(code)) Cisco Pass Mass - CatOS (deprecated by node 123464)".   Cleaner code.   Better error detection and handling, activity logging, and results reporting.

Commandline switches include:

 --help     print Usage() to screen
 --nochange reset pw's to original
 --ROcomm   will prompt for SNMP RO community string. Default of 'public'
 --target   accepts IPaddr, hostname or fqdn of single target
 --infile   accepts name of text file list of multiple targets.

Thanks to Petruchio for pre-post critique+comments+corrections, and to c for inspiration and example of Net::SNMP device-type detection from Pancho. Also to several monks who offered good counsel on prior efforts leading up to this project.

I'm seriously considering submitting this to Freshmeat and/or Sourceforge, so please chew it up and spit it out and let me know what's not tasty.
    cheers,
    Ned

#!/usr/bin/perl -w

# cpass
# pod at tail


$|++;                       # STDOUT hot
require 5;                  # run only on Perl v5 or newer
use strict;                 # avoid d'oh! bugs
use Net::SNMP;              # query target(s) for code type/ver
use Net::Telnet;            # required by Net::Telnet::Cisco
use Net::Telnet::Cisco;     # simplify telnet to Cisco devices
use Getopt::Long;           # support commandline switches
use Term::ReadKey;          # disable screen echo during password entr
+y
use Tie::IxHash;            # insertion-order hash of passwords+prompt
+s


######################################################################
+#####
# Season to taste:

# date-stamped filenames for uniquity.
my ($sec,$min,$hour,$mday,$mon,$year) = localtime(time);
my $ymd = sprintf("%04d%02d%02d",$year+1900,$mon+1,$mday);
my %file = (
   errorlog   => "cpass$ymd.err",
   sessionLog => "cpass$ymd.log",
   NtcLog     => "cpass$ymd.ntc",
   # NtcLog is Net::Telnet::Cisco log current session
   # !! contains cleartext IOS passwords !!
   # !! don't leave it laying around !!
);
my %parm = (
  NtcErrmode  => 'return', # Net::Telnet::Cisco not die on problem dev
+ice
  userTimeout => 120,      # secs to wait for keyboard input from user
  NtcTimeout  => 30,       # secs to wait for response from device
                           #   shorter for quicker prog run,
                           #   longer for small IOS routers slooow 'wr
+ mem'
);
my %snmp = (
  defROcomm => 'public',             # default Read-Only community str
+ing
  verOID    => '.1.3.6.1.2.1.1.1.0', # sysDescr for Cisco code version
);
my %id = (
  Ios      => 'Cisco Internetwork Operating System',
  CatOs    => 'Cisco Catalyst Operating System',
  nineteen => 'Cisco Systems Catalyst 1900',
);
my %counter = (
  targTotal   => 0,
  targIos     => 0,
  targCatOs   => 0,
  targUnknown => 0,
  telnetGood  => 0,
  telnetFail  => 0,
  loginGood   => 0,

  loginFail   => 0,

  enableGood  => 0,
  enableFail  => 0,
  IosGood     => 0,
  IosFail     => 0,
  CatOsGood   => 0,
  CatOsFail   => 0,
);
my (
  @IosGood,
  @CatOsGood,
  @IosFail,
  @CatOsFail,
  @telnetFail,
  @loginFail,
  @enableFail,
  @unknownFail,
);

my (
  $opt_help,
  $opt_nochange,
  $opt_ROcomm,
  $opt_target,
  $opt_infile,
);
GetOptions(
  'help!'     => \$opt_help,
  'nochange!' => \$opt_nochange,
  'ROcomm!'   => \$opt_ROcomm,
  'target=s'  => \$opt_target,
  'infile=s'  => \$opt_infile,
);


######################################################################
+#####
# Get stuf ready:

# Net::Telnet::Cisco input_log file accessible to only this user:
umask oct 177;

# Unlink prior logs since session log write is append:
for(keys %file) {
  if(-e $file{$_} and -f_){
    unlink($file{$_}) or die "Error unlinking $file{$_}: $!";
  }
}

open (LOG,"> $file{sessionLog}")
  or die "Error opening $file{sessionLog}: $!";

PrintLogConsole(
  "\n",
  " ** Started $0 **\n",
  "    ",DateTime(),"\n\n",
  );


if(defined $opt_help){
  Usage('You rang, sir?');
  CloseLog();
  exit;
}


######################################################################
+#####
# get list of target device(s)
my @targets;
if(defined $opt_target) {
  @targets = $opt_target;
  PrintLogConsole("  Target given at command-line switch.\n");
} elsif (defined $opt_infile) {
  $file{in} = $opt_infile;
  unless (-r $file{in} and -T _) {
    Usage("Error reading input file \"$file{in}\": \"$!\""); 
    CloseLog();
    exit;
  }
  PrintLogConsole("  Target list at $file{in}\n");
  open (INFILE, "< $file{in}") or die "Error opening $file{in}: $!";
  @targets = <INFILE>;
  close INFILE;
} else {
  Usage('Error - target device or input file not specified!');
  CloseLog();
  exit;
}


PrintLogConsole("  Validating target list... ");
for(@targets) {
  chomp;
  unless(/^(\w|-|\.)+$/) {
    Usage("Error - improperly formatted target name: $_");
    CloseLog();
    exit;
  }
  ++$counter{targTotal};
}
PrintLogConsole("done!\n\n");


######################################################################
+#####
# Set SNMP RO community string: either from user or default of "public
+".
my( $ROcomm, $input );
if(defined $opt_ROcomm) {
  PrintLogConsole("  Enter RO community string: ");
  WaitForUserInput();
  if($input =~ /^(\w|-|\.)+$/) {
    $ROcomm = $input;
  }
  else {
    PrintLogConsole(
      "Sorry, you have to enter *something* for SNMP RO community.\n",
      "Try again if you want.\n\n",
    );
    exit;
  }
  PrintLogConsloe("\n");
} else {
  $ROcomm = $snmp{defROcomm};
  }


######################################################################
+#####
# Get existing pw's from user:
PrintLogConsole(
  "  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";
for(keys %prompts) {
  PrintLogConsole("  $_ ");
  ReadMode('noecho');                     # don't echo password to scr
+een
  WaitForUserInput();
  $passwds{$prompts{$_}} = $input;
  ReadMode('restore');                    # re-activate screen echo
  PrintLogConsole("\n");
}

PrintLogConsole("\n");
my $oldpass       = ($passwds{"oldpass"});
my $oldenablepass = ($passwds{"olden"});


######################################################################
+#####
# Get replacement pw's from user:
my(
  $newpass,
  $newenablepass,
  $confirmpass,
  $confirmenablepass,
);

if(defined $opt_nochange) {
  PrintLogConsole(
    "  ###########################################\n",
    "  ### --nochange specified at commandline ###\n",
    "  ###   Passwords will *not* be changed   ###\n",
    "  ###########################################\n\n",
  );
  $newpass       = $oldpass;
  $newenablepass = $oldenablepass;
} else {
  PrintLogConsole(
    "  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";
  for(keys %newprompts) {
    PrintLogConsole("  $_ ");
    ReadMode('noecho');                 # don't echo password to scree
+n
    WaitForUserInput();
    $newpasswds{$newprompts{$_}} = $input;
    ReadMode('restore');                    # re-activate screen echo
    PrintLogConsole("\n");
  }

  # Confirm that pw's entered same both time:
  PrintLogConsole("\n");
  $newpass           = ($newpasswds{"newpass"});
  $confirmpass       = ($newpasswds{"confpass"});
  $newenablepass     = ($newpasswds{"newen"});
  $confirmenablepass = ($newpasswds{"confen"});
  unless (
    ($newpass eq $confirmpass)
    and
    ($newenablepass eq $confirmenablepass)
    ) {
    Usage('Error - password confirmation(s) did not match!');
    CloseLog();
    exit;
  }

}


######################################################################
+#####
# User review of target devices before proceeding:
PrintLogConsole ("  target devices:\n");
for(@targets) {
  PrintLogConsole ("   $_\n");
  }
PrintLogConsole(
  "\n",
  "  Ctrl+c to abort  or  <enter> to continue.",
);
WaitForUserInput();


######################################################################
+#####
# Get down to business:
my(
  $devtype,
  $ntc,
  @cmd_output,
);
for my $target(@targets) {

  ## Net::SNMP detection of device code type/ver
  my $ssess   = Net::SNMP->session(
    -hostname  => $target,
    -community => $ROcomm,
  );
  my $devver = $ssess->get_request ($snmp{verOID});
  my $error = $ssess->error;
  if($error) {
    ++$counter{targUnknown};
    push(@unknownFail, $target);
    PrintLogConsole(
      "\nError detecting $target device type: ${error}\nSkip to next.\
+n\n",
    );
    next;
  }
  if(($devver->{$snmp{verOID}}) =~ /$id{CatOs}/i){
    ++$counter{targCatOs};
    $devtype = 'CatOS';
  }
  elsif (($devver->{$snmp{verOID}}) =~ /$id{Ios}/i)  {
    $devtype = 'IOS';
    ++$counter{targIos};
  }
  elsif (($devver->{$snmp{verOID}}) =~ /$id{nineteen}/i)  {
    ++$counter{targUnknown};
    push(@unknownFail, $target);
    PrintLogConsole(
      "\nError: $target appears to be a Catalyst 1900.\nSkip to next. 
+\n\n"
    );
    next;
  }
  else {
    ++$counter{targUnknown};
    push(@unknownFail, $target);
    PrintLogConsole(
      "\nError: $target is of unknown device type.\nSkip to next. \n\n
+"
    );
    next;
  }
  $ssess->close;


  ## Telnet me baby:
  PrintLogConsole("  ",DateTime(),"\n");
  if($ntc = Net::Telnet::Cisco->new (
    host      => $target,
    errmode   => $parm{NtcErrmode},
    timeout   => $parm{NtcTimeout},
    input_log => $file{NtcLog},
  )) {
    ++$counter{telnetGood};
    PrintLogConsole ("    Connected to $target\n");
    if($ntc -> login('', $oldpass)){
      ++$counter{loginGood};
      PrintLogConsole (' 'x 4, $ntc -> last_prompt, "\n");
      if($ntc -> enable($oldenablepass)) {
        ++$counter{enableGood};
        PrintLogConsole (' 'x 4, $ntc -> last_prompt, "\n");
        SetPassIos($target) if $devtype eq 'IOS';
        SetPassCat($target) if $devtype eq 'CatOS';
      } else {
        ++$counter{enableFail};
        push(@enableFail, $target);
        PrintLogConsole (
          "\nError getting enable mode for $target.\nSkip to next.\n",
          ' 'x 4, $ntc -> last_prompt,
          "\n\n",
        );
      }
      # dunno why disable twice, but it works *shrug*
      $ntc -> cmd('disa');
      print(@cmd_output);
      $ntc -> disable;
      $ntc -> close;
    } else {
      ++$counter{loginFail};
      push(@loginFail, $target);
      PrintLogConsole("\nError logging in to $target\n\n");
    }
  } else {
  ++$counter{telnetFail};
  push(@telnetFail, $target);
  PrintLogConsole ("\nError connecting to $target\n\n");
  }
}


######################################################################
+#####
# Is that your final answer?

PrintLogConsole(
  "  ",DateTime(),
  "\n\n",
  "  Targets:\n",
  "    total:   $counter{targTotal}\n",
  "    IOS:     $counter{targIos}\n",
  "    CatOS:   $counter{targCatOs}\n",
  "    unknown: $counter{targUnknown} (skipped)\n",
  "\n",
  "  Telnet connections:\n",
  "    success  $counter{telnetGood}\n",
  "    error    $counter{telnetFail}\n",
  "\n",
  "  Logins:\n",
  "    success  $counter{loginGood}\n",
  "    error    $counter{loginFail}\n",
  "\n",
  "  Enable logins:\n",
  "    success  $counter{enableGood}\n",
  "    error    $counter{enableFail}\n",
  "\n",
  "  IOS devices reset:\n",
  "    success  $counter{IosGood}\n",
  "    error    $counter{IosFail}\n",
  "\n",
  "  CatOS passwords reset:\n",
  "    success  $counter{CatOsGood}\n",
  "    error    $counter{CatOsFail}\n",
  "\n",
);


# List, not count, of devices sucessfully reset, one per line:
my %successList = ( 
  'IOS devices sucessfuly reset'            => \@IosGood,
  'CatOS devices sucessfully reset'         => \@CatOsGood,
  'Unknown devices (no connection attempt)' => \@unknownFail,
  'Devices err on telnet connection'        => \@telnetFail,
  'Devices err on login'                    => \@loginFail,
  'Devices err on enable'                   => \@enableFail,
  'IOS devices err during reset'            => \@IosFail,
  'CatOS devices err during reset'          => \@CatOsFail,
);
for my $head (
  'IOS devices sucessfuly reset',
  'CatOS devices sucessfully reset',
  'Unknown devices (no connection attempt)',
  'Devices err on telnet connection',
  'Devices err on login',
  'Devices err on enable',
  'IOS devices err during reset',
  'CatOS devices err during reset',
) {
  PrintLogConsole("\n  $head:\n");
  PrintLogConsole("    $_\n") for Uniques( @{$successList{$head}} );

}
PrintLogConsole("\n\n");


CloseLog();

# NtcLog contains cleartext IOS passwords.
# !! Don't leave it laying around !!

if(-e $file{NtcLog} and -f_){
  unlink($file{NtcLog}) or die "Error unlinking $file{NtcLog}: $!";
}


# Parse log for errors:
print("  Parse $file{sessionLog} for errors\n");
open(GREPIN, "< $file{sessionLog}")
  or die "Error opening $file{sessionLog}: $!";
open(GREPOUT, "> $file{errorlog}")
  or die "Error opening $file{errorlog}: $!";

my @grepIn;
while(<GREPIN>){ push @grepIn, $_; }
for(my @grepOut = grep( /(^Error|^Note)/, @grepIn)){ print GREPOUT "$_
+"; }

close GREPIN  or die "Error closing $file{sessionLog}: $!";
close GREPOUT or die "Error closing $file{errorlog}: $!";


# Wrapitup:
print(
  "\n",
  " ** Completed $0 **\n",
  "    ",DateTime(),"\n\n",
  "    Session log: $file{sessionLog}\n",
  "    Error log:   $file{errorlog}\n",
  "\n",
  );


######################################################################
+#####
# Subroutines start here
######################################################################
+#####
sub SetPassIos {
  my $target = $_[0];

  # first Cisco config commands to be run:
  my @commands = (
    'term leng 0',
    'conf t',
    "enable pass $newenablepass",
  );

  # only reset pw's on VTYs already active/configured:
  my %sholine = (
    ' 0 CTY' => 'line con 0',
    ' 2 VTY' => 'line vty 0 4',
    ' 7 VTY' => 'line vty 5 9',
    '11 VTY' => 'line vty 10 15',
  );
  my @sholines = $ntc -> cmd('sho line');
  for(@sholines) {
    if(/1 AUX/) {
      PrintLogConsole(
        "Note: line '0 AUX' detected - pw not (re)set in case of DDR\n
+"
      );
    }
    for my $line(keys %sholine) {
      if(/$line/){
        print("      detected $line - set pw+login for $sholine{$line}
+\n");
        push(@commands, $sholine{$line}, "pass $newpass", 'login');
      }
    }
  }

  # last Cisco commands to be run:
  push(@commands, 'end','write mem');

  # run the Cisco commands:
  for my $command(@commands) {
    unless(my @output = $ntc -> cmd($command)){
      ++$counter{IosFail};
      push(@IosFail, $target, $command);
      return;
    }
  }
  ++$counter{IosGood};
  push(@IosGood, $target);
}

######################################################################
+#####
sub SetPassCat {
  my $target = $_[0];
  my( $old, $new);
  my @commands = (
    'set password',
    'set enablepass'
  );
  for my $command(@commands) {
    # print() instead of cmd() as 'set password' not return std prompt
    if($ntc -> print("$command")) {
      PrintLogConsole ("      $command\n");
    }
    else { return ("Error sending '$command' command to $target.\n"); 
+}
    # "$ntc -> getline" must be here to populate @getlines properly
    # I don't understand it, but is what made it work |^(
    my $getline  = $ntc -> getline;   # print "\$getline = $getline\n"
+;
    my @getlines = $ntc -> getlines;  # print ("\$getlines = $getlines
+\n");
    for(@getlines) {
      if(/% Invalid input/) {
    #for my $getlines(@getlines) {
    #  if($getlines =~ /% Invalid input/) {
        push(@CatOsFail, $target);
        PrintLogConsole ("Error running '$command' command at $target.
+\n");
        PrintLogConsole (' 'x 4, $ntc -> last_prompt, "\n");
        return();               # skip to next target if command 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:/')) {
      $ntc -> print($old);
      print(@cmd_output);
    } else {
      ++$counter{CatOsFail};
      push(@CatOsFail, $target);
      return PrintLogConsole (
        "Error getting 'Enter old password' prompt.\n"
        );
    }
    if($ntc ->waitfor('/Enter new password:/')) {

      $ntc -> print($new);
      print(@cmd_output);
    } else {

      ++$counter{CatOsFail};
      push(@CatOsFail, $target);
      return PrintLogConsole (

        "Error getting 'Enter new password' prompt.\n"
      );
    }
    if($ntc ->waitfor('/Retype new password:/')) {
      $ntc -> print($new);
      print(@cmd_output);
    } else {
      ++$counter{CatOsFail};
      push(@CatOsFail, $target);
      return PrintLogConsole (
        "Error getting 'Retype new password' prompt.\n"
      );
    }
    # Woohoo - it worked!
    if($ntc ->waitfor('/Password changed./')) {
      ++$counter{CatOsGood};
      push(@CatOsGood, $target);
    } else {
      ++$counter{CatOsFail};
      push(@CatOsFail, $target);
      return PrintLogConsole (
        "Error getting 'Password changed' prompt.\n"
      );
    }
  }
}

######################################################################
+#####
# extract unique elements of array
sub Uniques {
  my %saw = map { $_ => 1 } @_;
  sort keys %saw;
}

######################################################################
+#####
sub DateTime {
  my($sec,$min,$hour,$mday,$mon,$year) = localtime(time);

  my $datetime = sprintf( "%04d-%02d-%02d %02d:%02d:%02d",
    $year+1900,$mon+1,$mday,$hour,$min,$sec);
}


######################################################################
+#####
sub WaitForUserInput {
  eval {
    local $SIG{ALRM} = sub { die "ALARUM" };
    alarm("$parm{userTimeout}");
    chomp($input = <STDIN>);
    alarm(0);
  };
  if($@ =~ /ALARUM/) {
    print "\n\n";
    print "Sorry - You waited too long before entering something.\n";

    print "Try again if you want.\n\n";
    ReadMode('restore');                    # re-activate screen echo
    exit;         # "exit" instead of "die" so no error to console
  }
}

######################################################################
+#####
# Print to console and logfile
# param is text string to be printed
sub PrintLogConsole { 
  print @_;
  print(LOG @_) or die "Error printing to $file{sessionLog}: $!";
}

######################################################################
+#####
sub CloseLog {
  close LOG or die "Error closing $file{sessionLog}: $!";
}

######################################################################
+#####
# Really don't have to esplain this'un, eh?
sub Usage {
  my $specific_err = $_[0];
  print <<EOF;

 $specific_err


 Usage:
 cpass.pl --help --nochange --ROcomm --target hostname --infile filesp
+ec

 Switches include ("=" is optional):
  --help                  *or*  -h
  --nochange              *or*  -n
  --ROcomm                *or*  -R
  --target=myputer        *or*  -t 172.31.0.5
  --infile /path/infile   *or*  -i=/path/infile

 "help" displays this very message screen.

 "nochange" resets passwords to original values).


 "ROcomm" option causes program to prompt you for an SNMP Read-Only
  community string.  Default is 'public'.

 You *must* specify either target or infile.

 If "target" given, then infile switch is ignored.
 Target can be IP address, hostname, or FQDN.
 Valid characters include alphanumeric, underscore, dash and dot.

 "infile" is a text file of target devices.
 One device per line.
 No blank lines.
 Same character limitations as with 'target'.


 Currently installed versions of necessary Perl modules:
  Net::Telnet::Cisco $Net::Telnet::Cisco::VERSION
  Net::Telnet        $Net::Telnet::VERSION
  Net::SNMP          $Net::SNMP::VERSION
  Term::ReadKey      $Term::ReadKey::VERSION
  Tie::IxHash        $Tie::IxHash::VERSION

  Perl               $]
  OS                 $^O
  Program            $0

EOF
  ;
}


######################################################################
+#####

=head1 TITLE

 cpass.pl

=head1 DESCRIPTION

 Automate password resets for multiple Cisco switches and/or routers.

 Runtime is a couple seconds for each device.

 CatOS = 2948g, 4000, 5000 and 6000
 IOS   = 2900xl, 2950, 3500, and routers
 ???   = 1900

=head1 USAGE

 Use the source, Luke.
 Or run cpass.pl with no switches.

=head1 TESTED

 with:
  Debian              2.2r(2|3) "Espy"
  Perl                5.00503
  Term::ReadKey       2.14
  Tie::IxHash         1.21
  Net::SNMP           3.65
  Net::Telnet         3.02
  Net::Telnet::Cisco  1.03

 against:
  2514   12.0(8)
  2900xl 11.2(8.5)SA5, SA6
  3500   12.0(5.1)XP
  2948g  4.5(3), (4)

 retest:
  4006   5.5(1)
  5505   4.5(1)
  5500   4.5(2)
  6009   5.3(5a)CSX
  6509   5.5(1)

=head1 UPDATED

 2001-11-05   21:15
  Post to PerlMonks for review.
  Move "CloseLog();exit;" lines out of &Usage.
  Unlink logs at start with for(keys %file).
  Unlink errorlog at start.
  Untie %file, as insertion-order retrieval not needed.
  Detect Catalyst 1900 and skip.
  Datestamp as part of log file names.
  grep main log for ^Error to populate errorlog.
  Remove explicit $_ where already already implied.
  Disply VTYs detected *and* resulting VTYs pw-reset.
  Untie %sholine as is sorted with keys(%sholine).
  Require Perl v5 or higher.
  Judicious add of clarifying comments.
  Tied-hashimafy @sholines to eliminate redundent if loops.
  Eliminate global var $target.
  Copy "my $target = $_[0];" from &SetPassIos to &SetPassCat.
  Remove unecessary quotes.
  Reduce indents from 2 spaces to 3.
  Simplify multiple PrintLogConsole(); to PrintLogConsole(,,,);

 2001-11-03   21:25
  PrintLogConsole() *all* screen messages.
  HOL for final log/viewing success/fail.
  Extract uniques from @*(Good|Fail) for log+console at end.
  Push login/enable error devices to @(Ios|CatOs)Fail.
  Replace Pause() with WaitForUserInput() for timeout.
  Error detection+handling for non-privilaged login.
  IOS reset counter   - no err during 'conf t' session and 'wr m' not 
+err.
  CatOs reset counter - no err and *both* access+enable pw's reset.
  20sec min timeout for IOS router 'write mem'.
  'write mem' instead of 'copy run st' for IOS routers.
  Check for number of IOS VTYs, only reset pw's on that many.
  Check for presence of IOS 'line aux 0', only attempt reset pw if pre
+sent.
   (present for routers, not for switches)
  Sanity-check user-provided SNMP ROcommunity string.
  Timeout waiting for user input if --ROcomm.
  Prompt instead of switch for SNMP community if --ROcomm.
  CatOS success/fail count.
  Handle IOS switches/routers as well as CatOS switches.
  Check for CatOS/IOS with SNMP 'sho ver'.
  Getopt param --help.
  Initial psuedo-code for (CatOS|IOS) detection with Net::SNMP.
  Unlink Net::Telnet::Cisco input_log when run complete.
  s/&&/and/g
  --nochange to leave passwords as is.
  use Getopt::Long for input params.
  Simplify timestamp chunk.
  Change output indenting so headers closest to left margin.
  Un-subamify functions only called once.
  Replace tabs with '   '.
  $_ with simple loops instead of 'for my $foo(@foos)'.
  'my' instead of 'use vars'.
  s/foreach/for/g
  STDOUT hot.
  Indent to match PerlStyle recommendations.
  Remove '&' from subroutine names.
  Rename to cpass.

  2001-10-28
  Begin rewrite of SetPassCat.

=head1 TODO

 Testing:
  Retest against more (IOS|CatOS) (switch|router) versions.

 Functionality:
  Properly handle wrong login pw for CatOS devices.
  Count+display CatOS devices (not passwords) reset.
  IOS Getline and Waitfor for better error detection+handling.
  Display and log elapsed time, start-to-finish.
  Support multiple targets with --target.
  Support 1900 Catalysts with their stupid menu and only one password.
  Ping targets for reachability before SNMP device type query.
    Net::Ping  or  Net::Ping::Improved
  Improve (DNS name|IP address) check for infile sanity-checking.

    Net::IPv4Addr - ipv4_checkip
  Check for (and skip if) TACACS-enabled device.
    prompt =~ /sername/

 Code quality:
  Test::(Simple|More) test frame:
    cpass.t/foo.t, bar.t, bat.t...
  Real versioning, not just Update datestamp.
  Post to (FreshMeat|SourceForge).
  Untaint inputs instead of just sanity check.
  Pod::Usage to synchronize Usage() with pod.
  ?? Figure out array or hash for installed modules::VERSIONS.
  ?? use User.pm to save (log|summary) files to home dir.

=head1 BUGS

 -n doesn't work, while --nochange works fine.

=head1 AUTHOR

 ybiC

=head1 CREDITS

 Thanks to:
  Petruchio for code reviews, muchly++ suggestions & corrections.
  c for inspiration+code from Pancho for Net::SNMP to detect device ty
+pe.
  to several other monks who offered good counsel on prior efforts lea
+ding up to this project.
  vroom for http://www.perlmonks.org

=cut
Replies are listed 'Best First'.
Re: (code) Poor Man's TACACS - automate CatOS and IOS password resets with Net::Telnet::Cisco and Net::SNMP
by Anonymous Monk on Oct 12, 2007 at 22:40 UTC
    s/Consloe/Console/

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://123464]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2024-03-19 11:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found