#!/opt/vdops/bin/perl

# This script checks to see if it can ftp to a device using the default 
# username & password.  If it can, then it sends the operator a list of 
# these 'default accessible' devices

# V      Who       When        What
# ---------------------------------------------------------------------------
# 1.1.0  skendric  2008-10-20  Don't try to fix the problem, just mail the 
#                              results
# 1.0.7  skendric  2008-05-28  Log behavior
# 1.0.6  skendric  2007-04-11  Simplify invocation of apc-mod-config
# 1.0.5  skendric  2007-03-21  Stylistic mods
# 1.0.4  skendric  2007-02-27  Change 'apc-config' to 'apc-mod-config'
# 1.0.3  skendric  2006-12-12  Stylistic mods
# 1.0.2  skendric  2006-08-04  More error checks
# 1.0.1  skendric  2005-11-05  Upgrade to new FHCRC::VDOPS module structure
# 1.0.0  skendric  2005-10-12  First Version
#

# Author:  Stuart Kendrick, sbk {put at sign here} skendric {put dot here} com
#
# Source:  http://www.skendric.com/device
#
# This software is available under the GNU GENERAL PUBLIC LICENSE, see
# http://www.fsf.org/licenses/gpl.html
#

#
# This script takes the following approach:
#     -Test for default username/password
#     -Upload the config file snippet via FTP
#
# Requirements:
#     -The following MIB modules stashed in /opt/vdops/share/snmp/mibs,
#      or wherever it is that you store MIB modules:
#      PowerNet-MIB
#
#     -PERL modules:  the FHCRC::Netops collection
#
#
# Assumptions:
#
#
# Tested on: 
#         -APC 9606 Web/SNMP card, APC 961x Network Management Card,
#          SmartUPS, Symmetra, Silcon 
#         -perl-5.8.8
#         -Net-SNMP-5.2.0
#
#
# Instructions:
#     -Create an AP960x config file using the i2c301.exe utility
#      (see ftp://ftp.apcc.com/apc/public/hardware/webcard/firmware/sumx/
#       v321/addendum.pdf for instructions.  See ../webcard/i2c for the
#       utility itself.)
#     -Or, create an AP961x/AP963x config file, which is simply a text file 
#      following a standard format and typically named 'config.ini'
#     -Customize the script for your site:  find the 'user-configurable
#      variables' section and modify as appropriate
#     -Type "apc-reset-passwd" to see the command-line options
#     -Try it out
#
#
# Caveats:
#
#
# Known Bugs:
#     -If the FTP server on the mangement card is disabled, the code
#      bombs.  I'm trying to use 'eval' to trap this error, but it
#      doesn't seem to work.
#
#
# To do:
#

# Begin script

# Load modules
use strict;
use warnings;
use 5.8.1;
use Carp qw(carp cluck croak confess); 
use Data::Dumper;  
use English;
use Getopt::Std;
use Net::FTP;
use Perl6::Builtins qw( system );
use Perl6::Say;
use Switch;
use FHCRC::Netops::APCTools 1.0.9;
use FHCRC::Netops::HostTools 1.0.2;
use FHCRC::Netops::NetopsTools 1.8.7;
use FHCRC::Netops::NetopsData 1.2.1;
use FHCRC::Netops::PingTools 1.1.3;
use FHCRC::Netops::SNMPTools 1.3.0;
use FHCRC::Netops::Utilities 1.2.9;

# Declare global variables
my @accessible;          # List of devices which permitted me to log in
                         # using default username/password
my %auth;                # Hash keyed by username of username and 
                         # password foo

# Define global variables
$debug = 0;
            # 10 = Logging
            #  9 = Database SELECT operations
            #  8 = Per IP/MAC/Port processing
            #  7 = Database INSERT/UPDATE/DELETE
            #  6 = Dump SNMP var
            #  5 = Dump snmp_packets
            #  4 = Grody:    print big var
            #  3 = Verbose:  print mid var
            #  2 = Simple:   print small var
            #  1 = Basic:    subroutine trace
            #  0 = Disable debugging
$program_name = 'apc-reset-passwd';
$usage = 'Usage:  apc-reset-passwd -s yes|no [-d {integer}] [-a | -e {expr} | -f {filename} | target1 target2 target3 ...]';
$version = '1.1.0';

# Define user-configurable variables

# Binaries
$grab_hosts   = '/bin/cat /etc/hosts';
#$grab_hosts    = '/usr/bin/niscat hosts.org_dir';

# Pause parameters
$long  = 30;
$mid   = 10;
$short = 5;

# Ping Stuff
$ping_count   = 3;
$ping_timeout = 1;

# Default username/password
%auth = (
          admin => 'apc',
          apc   => 'apc',
        );

# Report stuff
$owner             = 'Stuart Kendrick';
$owner_backup      = 'Terry Lu';
$report_recipients = 'operators@widgets.com';
$report_subject    = 'Reset-APC-Passwd Report';

# SNMP Stuff
# Optimize performance by sorting your community strings and SNMP version
# list, most frequently used to the left, least frequently used to the right
@mib_dir           = qw(/opt/vdops/share/snmp/mibs);
@mib_file          = qw/PowerNet-MIB/;
@snmp_read_list    = qw/not-secret public public/;
@snmp_version_list = qw/2 1/;
$snmp_port         = 161;
$snmp_retries      = 3;
$snmp_timeout      = 1;

# Syslog stuff
$syslog_facility  = 'local5';
$syslog_host      = 'localhost';
$syslog_port      = 514;        
$syslog_priority  = 'info';
$syslog_socket    = 'unix';    # Other possibilities include 'udp' and
                               # 'stream'; depending on the flavor of Unix,
                               # I've employed each of these

# Target details
@skip_name = qw/crds dej-vpn impac-vpn mimi pgh pms-vpn rodolfo sod-vpn swamp/;
@suffixes  = qw/-bat -emu -pdu -ups/;

# Grab arguments
getopts('ad:c:e:f:s:', \%option); 
@target = @ARGV;

# Set mode
if      ($option{r})    { $mode = 'report'      }
elsif   (-t STDIN)      { $mode = 'interactive' }
else                    { $mode = 'batch'       }


### Begin Main Program ###############################################
{

  check_args();                   # Check arguments

  compile_mibs();                 # Compile MIB files

  build_target();                 # Populate @target

  target_check();                 # Look for errors in @target

  basic_info();                   # Gather information

  info_before();                  # Identify accessible devices

  tell_staff();                   # Tell staff what happened

}
##### End Main Program ###############################################


########################################################################
# Figure out which devices are accessible via default username/password
########################################################################
sub info_before {
  my $ftp;               # Net::FTP object
  my $val;               # Result of Net::FTP operations

  # Debug trace
  trace_location('begin') if $debug;

  # Notify operator
  print_it("Checking for accessibility via default username/password...");

  # Loop through targets, add accessible devices to @accessible
  for my $target (@target) {
  
    # Loop through default auth information
    for my $username (keys %auth) {
      my $password foo

      eval { $ftp = Net::FTP ->new     ($target, Debug => $debug) };
      if ($@ eq $EMPTY_STR) {
        eval { $val = $ftp ->login     ($username, $password) };
        push @accessible, $target if $val;
        eval { $val = $ftp ->quit };
      }

    }

    # Entertain operator
    print $BANG if $mode eq 'interactive';

  }

  # Make things look pretty
  say "\n" if $mode eq 'interactive';

  # Log results
  if (@accessible > 0) {
    my $list = join $SPACE, @accessible;
    print_it("The following devices permit default logins: $list");
  }
  else {
    print_it("No targets permitted default logins, exitting");
    exit 1;
  }

  # Debug trace
  trace_location('end') if $debug;

  return 1;
}



########################################################################
# If any devices need reconfiguration, tell staff
########################################################################
sub tell_staff {                  
  my $fh;
  my $msg;

  # Debug trace                   
  trace_location('begin') if $debug;

  # Unless I'm being watched already, send staff e-mail
  unless ($mode eq 'interactive') {

    if (@accessible > 0) {

      # Build message header
      $msg = Mail::Send->new();
      $msg->to($report_recipients);
      $msg->subject($report_subject);

      # Build message body
      $fh = $msg->open;
      print $fh "The following devices permit default access, please update their configurations.  $owner owns this list; $owner_backup is backup\n";
      for my $accessible (@accessible) {
        print $fh "$accessible\n";
      }

      # Send message
      $fh->close;

    }

  }

  # Make things look pretty
  log_it("Ending $PROGRAM_NAME");
  say "Ending $PROGRAM_NAME" if $mode eq 'interactive';

  # Debug trace
  trace_location('end') if $debug;

  return 1;
}




########################################################################
# Output help
########################################################################
sub HELP_MESSAGE {
  print <<EOF;

  $usage

    -s asks the question:  are you serious?  If you answer 'no', then 
       the script will run in demo mode, making no changes

    -d specifies debug level (typically 0-9)

    -a instructs the script to filter the hosts table using its internally
       defined suffixes variable to produce a list of targets

    -c specifies the config file snippet which we will upload, to devices 
       which permit default logins

    -e requires a regex as its argument, with which the script filters
       the hosts table to produce a list of targets.  Enclose the regex in 
       quotes to protect special characters from the shell

    -f points the script to a file containing the list of targets

    target1 target2 target3 ... is the command-line list of targets you
      have specified, in lieu of the -a, -e, or -f options

EOF

  return 1;
}




########################################################################
# Output version
########################################################################
sub VERSION_MESSAGE {
  say "$program_name v$version";
  return 1;
}
