#!/usr/local/bin/perl 
#
# HPUX    - !/opt/perl5/bin/perl
# Linux   - !/usr/local/bin/perl
# AIX     - !/usr/local/bin/perl
# Solaris - !/usr/local/perl5.6.1/bin/perl
#=========================================================================#
# Program: check_security                                                 #
#  Author: Chad Kerner                                                    #
#=========================================================================#
# Purpose: The purpose of this program was to create a single utility     #
#          that could be used to check security across the board on all   #
#          of the systems in CMI.  This program was modeled after the     #
#          shell script received from Israel.                             #
#=========================================================================#
# Note: This must be run with perl 5.6.1 or greater.                      #
#=========================================================================#
# Program Options:                                                        #
# -help         Display this help screen.                                 #
# -all		Toggle all of the security checks to run.                 #
# -hourly       Toggle the hourly checks to run.                          #
# -daily        Toggle the daily checks to run.                           #
# -vpo          Use this switch to log messages to VPO.                   #
# -fix          Attempt to correct the errors that have been found.       #
# -rhosts	Check the permissions on the .rhost files.                #
# -netrc	Check the permissions on the .netrc files.                #
# -sshkey	Check the permissions on the ssh2 private key files.      #
# -samba	Check to see if samba is running.  If so, check the       #
#               security of the configuration.                            #
# -http		Check to see if httpd is running.  If so, check the       #
#               security of the configuration.                            #
# -ftp          Checks to see if the tftp daemon has been configured on   #
#               the system.  If so, print the configuration to the report.#
# -secure	Check to see if the secure script has ran on this system. #
# -passwd	Check the /etc/passwd file for several criteria.          #
# -setuid	This routine will check for setuid files.                 #
# -setgid	This routine will check for setgid files.                 #
# -trusted	Check to see if the system is executing in trusted mode.  #
# 		Note that this is only valid for HP-UX systems.           #
# -equiv        Check to see if the /etc/hosts.equiv file exists.  If so, #
#               verify that the permissions are set to 600.               #
# -http         Check to see if the httpd daemon is running on the system.# 
# -system       If you have any system specific checks, they can be ran   #
#               from here.                                                #
# -exports      Check the /etc/exports file for correct permissions and   #
#               to see if the access parameter has been set for each of   #
#               specified mount points.                                   #
# -skel         Check the permissions on the files in /etc/skel to see if #
#               they are set properly.                                    #
# -shells       This will check to see if the shells in /etc/shells are   #
#               actually on the system.  If so, it will check the file    #
#               permissions to make sure that it is executable by all.    #
#=========================================================================#
# Modification History                                                    #
# 20021018 Chad Kerner    - Initial Coding                                #
# 20021021 Chad Kerner    - Coded a -fix option to correct some of the    #
#                           common file permission errors.                #
# 20030108 Chad Kerner    - Changed the /etc/shells logic to accomodate   #
#                           the way that AIX works.                       #
#=========================================================================#


# Package Declarations
use strict;
use Getopt::Long;
use POSIX qw(strftime);


# Function Prototypes
sub start_prog();
sub end_prog();
sub init_vars();
sub check_rhosts();
sub check_sshkey();
sub toggle_all_options();
sub toggle_hourly_options();
sub toggle_daily_options();
sub check_rhosts();  
sub check_netrc();   
sub check_samba();   
sub check_equiv();   
sub check_http();    
sub check_ftp();     
sub check_passwd();  
sub check_secure();   
sub check_exports(); 
sub check_files();
sub check_system();
sub check_trusted();
sub check_hpux();
sub check_osf();
sub check_sun();
sub check_aix();
sub check_linux();
sub check_setuid();
sub check_setgid();
sub check_skel();
sub check_shells();
sub print_help_screen();
sub log_msg($$);
sub return_perms($);
sub return_mode($);
sub return_user($);
sub return_group($);
sub convert_mode($);
sub set_special_files();
sub set_perms($$);


# Variable Declarations
$main::logfile = "/tmp/security.log";
@main::args = @ARGV;


# Main Routine 
{
    init_vars();

    log_msg(1,"\n\n* * * Security Audit Report - $main::hostname * * * \n");
    log_msg(1, "  Audit Timestamp: $main::time");
    log_msg(1, " Operating System: $main::os_type");
    log_msg(1, " Writing Log File: $main::logfile");
    log_msg(1, "Command Arguments: @main::args\n");

    if( $main::rhosts eq "1"  ) { check_rhosts();  }
    if( $main::sshkey eq "1"  ) { check_sshkey();  }
    if( $main::netrc eq "1"   ) { check_netrc();   }
    if( $main::samba eq "1"   ) { check_samba();   }
    if( $main::equiv eq "1"   ) { check_equiv();   }
    if( $main::http eq "1"    ) { check_http();    }
    if( $main::ftp eq "1"     ) { check_ftp();     }
    if( $main::passwd eq "1"  ) { check_passwd();  }
    if( $main::shells eq "1"  ) { check_shells();  }
    if( $main::secure eq "1"  ) { check_secure();  }
    if( $main::exports eq "1" ) { check_exports(); }
    if( $main::trusted eq "1" ) { check_trusted(); }
    if( $main::skel eq "1"    ) { check_skel();    }
    if( $main::system eq "1"  ) { check_system();  }
#    if( $main::files eq "1"   ) { check_files();   }
    if( $main::setuid eq "1"  ) { check_setuid();  }
    if( $main::setgid eq "1"  ) { check_setgid();  }

    log_msg(1,"\n\n* * * End Of The Security Audit Report * * * \n");

    end_prog();

    exit;
} # End of the main code segment.


#-------------------------------------------------------------------------#
# This routine firsts checks to make sure the system is running HP-UX as  #
# its operating system.  If so, it checks for the existence of one of the #
# required files for trusted mode: /tcb/files/auth/system/default.  If    #
# the file exists, the system is in trusted mode.  If not, then it is not #
# executing in trusted mode.                                              #
#-------------------------------------------------------------------------#
sub check_trusted() {
    log_msg(1,"\nChecking to see if the system is executing in trusted mode.");
    if( $main::os_type eq "HP-UX" ) { 
        if( -e "/tcb/files/auth/system/default" ) { log_msg(1,"$main::hostname is executing in trusted mode."); } 
        else { log_msg(1,"$main::hostname is not executing in trusted mode."); } } 
    else { log_msg(1,"Can not check for trusted mode on non-HP systems."); } 
    return;
} # End of the check_trusted() subroutine.


#-------------------------------------------------------------------------#
# This routine will check each user's home directory for a .rhosts file.  #
# If the file exists, it will check the permissions on the file and make  #
# sure that the permissions are set at 660 (-rw-------).                  #
#-------------------------------------------------------------------------#
sub check_rhosts() {
    log_msg(1,"\nChecking permissions .rhost files."); 
    foreach (@main::passwd) {
        chomp;
        my ($user,$passwd,$uid,$gid,$info,$homedir,$shell) = split /:/;
        my $fname = "$homedir" . "/.rhosts";
        if( -e $fname ) {
            my $permissions = return_perms($fname);
            if( substr($permissions,3,6) ne "------" ) { 
                log_msg(3,".rhosts file for $user has invalid permissions - $permissions."); 
                if( $main::fix eq "1" ) { set_perms(600,$fname); } 
            }
        }
    }
    return; 
} # End of the check_rhosts() subroutine.

 
#-------------------------------------------------------------------------#
# This routine will check each user's home directory for a .netrc file.   #
# If the file exists, it will check the permissions on the file and make  #
# sure that the permissions are set at 600 (-rw-------).                  #
#-------------------------------------------------------------------------#
sub check_netrc() { 
    log_msg(1,"\nChecking permissions on .netrc files.");
    foreach (@main::passwd) {
        chomp;
        my ($user,$passwd,$uid,$gid,$info,$homedir,$shell) = split /:/;
        my $fname = "$homedir" . "/.netrc";
        if( -e $fname ) {
            my $permissions = return_perms($fname);
            if( substr($permissions,3,6) ne "------" ) { 
                log_msg(3,".netrc file for $user has invalid permissions - $permissions.");
                if( $main::fix eq "1" ) { set_perms(600,$fname); } 
            }
        }
    }
    return; 
} # End of the check_netrc() subroutine.


#-------------------------------------------------------------------------#
# This routine will check each user's home directory ito see if their ssh #
# private key exists.  If so, it will check the permissions on the file   #
# and make sure that the permissions are set at 600 (rw-------).          #
#-------------------------------------------------------------------------#
sub check_sshkey() { 
    log_msg(1,"\nChecking permissions on ssh2 private key files.");
    foreach (@main::passwd) {
        chomp;
        my ($user,$passwd,$uid,$gid,$info,$homedir,$shell) = split /:/;
        my $fname = "$homedir" . "/.ssh2/id_dsa_1024_a";
        if( -e $fname ) {
            my $permissions = return_perms($fname);
            if( substr($permissions,3,6) ne "------" ) { 
                log_msg(3,"ssh private key for $user has invalid permissions - $permissions.");
                if( $main::fix eq "1" ) { set_perms(600,$fname); } 
            }
        }
    }
    return; 
} # End of the check_sshkey() subroutine.


#-------------------------------------------------------------------------#
# This routine looks for the samba configuration file.  If it finds it,   #
# it will then check the permissions on the file to verify that they are  #
# set at 644.  It also checks to see if the samba process is running.     #
#-------------------------------------------------------------------------#
sub check_samba() { 
    my $smbfile;
    log_msg(1,"\nCheck to see if samba is running.");
    if( $main::os_type eq "HP-UX" )    { $smbfile = "/usr/local/samba/lib/smb.conf"; }
    elsif( $main::os_type eq "Linux" ) { $smbfile = "/etc/samba/smb.conf"; }

    my @smbd = grep /smbd/i, @main::pslist;
    if( scalar @smbd > 0 ) { log_msg(3,"Warning: The samba daemon (smbd) is executing on this system."); }
    else                   { log_msg(1,"The samba daemon (smbd) is not running on this system."); }

    if( -e "$smbfile" ) {
        my $permissions = return_mode($smbfile);
        if( $permissions ne "644" ) { 
            log_msg(3,"Warning: Invalid permissions($permissions) on $smbfile."); 
            if( $main::fix eq "1" ) { set_perms(644,$smbfile); } 
        }
    }
    return; 
} # End of the check_samba() subroutine.


#-------------------------------------------------------------------------#
# This routine checks to see if there is an /etc/hosts.equiv file.  If so #
# it will check the permissions on the file to verify that they are set   #
# correctly.                                                              #
#-------------------------------------------------------------------------#
sub check_equiv() {
    log_msg(1,"\nChecking to see if /etc/hosts.equiv exists.");
    if( -e "/etc/hosts.equiv" ) {
        log_msg(4,"Warning: This system has a /etc/hosts.equiv file.");
        my $permissions = return_mode("/etc/hosts.equiv");
        if( $permissions ne "600" ) { 
            log_msg(3,"Warning: Invalid permissions($permissions) on /etc/hosts.equiv."); 
            if( $main::fix eq "1" ) { set_perms(600,"/etc/hosts.equiv"); } 
        }
    }
    else { log_msg(1,"/etc/hosts.equiv does not exist on this system."); }
    return;
} # End of the check_equiv() subroutine.


#-------------------------------------------------------------------------#
# This routine checks to see if the httpd process is running.  We do not  #
# want any web servers running on the systems.                            #
#-------------------------------------------------------------------------#
sub check_http() { 
    log_msg(1,"\nCheck to see if httpd is running.");
    my @httpd = grep /httpd/i, @main::pslist;
    if( scalar @httpd > 0 ) { log_msg(3,"Warning: The http daemon (httpd) is executing on this system."); }
    else { log_msg(1,"The httpd daemon is not running on this system."); }
    return; 
} # End of the check_http() subroutine.


#-------------------------------------------------------------------------#
# This routine checks the /etc/inetd.conf file to see if the tftp service #
# has been configured.  If so, it prints its configuration.               #
#-------------------------------------------------------------------------#
sub check_ftp() {
    log_msg(1,"\nCheck to see if tftp is configured."); 
    if( $main::os_type eq "Linux" ) { 
        if( -e "/etc/xinetd.d/tftp" ) {
            open(INFILE,"/etc/xinetd.d/tftp");
            my @tftp = <INFILE>;
            close(INFILE);
            foreach(@tftp) {
                chomp;
                my ($var1,$var2,$var3,$var4) = split;
                if( $var1 eq "disable" && $var3 eq "no" )    { log_msg(1,"The tftp service is configured."); }
                elsif( $var1 eq "disable" && $var3 eq "yes" ) { log_msg(1,"The tftp service is not configured."); }
            }
        }
    }
    elsif( -e "/etc/inetd.conf" ) {
        open(INFILE,"/etc/inetd.conf");
        my @inetd = <INFILE>;
        close(INFILE);
        my $tftp_conf = "";
        foreach (@inetd) {
            chomp;
            if( substr($_,0,4) eq "tftp" ) { $tftp_conf = $tftp_conf . $_; }
            if( $tftp_conf ne "" && substr($_,0,1) eq " ") { $tftp_conf = $tftp_conf . $_; }
        }
        if( $tftp_conf ne "" ) { log_msg(1,"TFTP config: $tftp_conf"); }
        else { log_msg(1,"The tftp service is not configured."); }
    }
    else { log_msg(1,"The tftp service is not configured."); }
    return;
} # End of the check_ftp() subroutine.


#-------------------------------------------------------------------------#
# This routine performs various checks on the /etc/passwd file.           #
#-------------------------------------------------------------------------#
sub check_passwd() { 
    log_msg(1,"\nCheck various rules against the /etc/passwd file.");
    my $counter = 1;
    my %dups = ();
    foreach (@main::passwd) {
        chomp;
        my $saveline = $_;

        # Check for blank lines in the password file.
        if( $_ eq "" ) { log_msg(2,"Warning: Line $counter in /etc/passwd is a blank line."); }
        else {
            my ($user,$passwd,$uid,$gid,$info,$homedir,$shell) = split /:/;

    	    # Slot the user into a hash to check for duplicate accounts.
            $dups{$user} = $dups{$user} + 1;

            # Check to see if the user name is longer than 8 characters.
            if( $uid >= 100 && length($user) > 8 ) { 
                log_msg(3,"Warning: USERNAME $user > 8 characters.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Check to see if the user name contains special characters.
            if( $user =~ m/\W+/ ) { 
                log_msg(3,"Warning: USERNAME $user contains special characters.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Check to see if the password is blank.
            if( $uid >= 100 && length($passwd) == 0 ) {
                log_msg(3,"Warning: Password for $user is blank.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Look for non-root accounts with UID=0
            if( $uid eq "0" && $user ne "root" ) {
                log_msg(3,"Warning: User $user has a UID of 0.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Verify that UID is numeric.
            if( $uid =~ m/\D+/ ) {
                log_msg(3,"Warning: Non-numeric UID($uid) for $user.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Verify that GID is numeric.
            if( $gid =~ m/\D+/ ) {
                log_msg(3,"Warning: Non-numeric GID($gid) for $user.  Line $counter in /etc/passwd.");  
                log_msg(1,"         $_"); }

            # Check to see if the userid ftp exists.
            if( $user eq "ftp" || $user eq "tftp" ) {
                log_msg(3,"Warning: Userid $user should not exist.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Check to see if the home directory is a valid directory.
            if( ! -d "$homedir" && $uid >= 100 ) {
                log_msg(3,"Warning: User $user has an invalid home directory: $homedir.  Line $counter in /etc/passwd."); 
                log_msg(1,"         $_"); }

            # Check to see if shell is a valid shell from /etc/shells.
            # if( scalar @main::shells > 0 ) {
            if( -e "/etc/shells" || -e "/etc/security/login.cfg" ) {
                my @shl = grep /$shell/, @main::shells;
                if( scalar @shl == 0 && $uid >= 100 ) {
                    log_msg(3,"Warning: User $user has an invalid shell($shell).  Line $counter in /etc/passwd."); 
                    log_msg(1,"         $_"); }
            }
        }
        $counter = $counter + 1;
    }

    # Check the hash for duplicate accounts.
    foreach (keys %dups) { if( $dups{$_} > 1 ) { log_msg(3,"Warning: Duplicate account($dups{$_}) for $_."); } }

    return; 
} # End of the check_passwd() subroutine.


#-------------------------------------------------------------------------#
# This routine will check the /etc/motd file to see if it contains the    #
# word unauthorized.  If so, it is assumed that the secure.sh script has  #
# been executed on this system.                                           #
#-------------------------------------------------------------------------#
sub check_secure() { 
    log_msg(1,"\nCheck to see if the security script has ran.");
    my @motd = `grep -i "unauthorized" /etc/motd 2>/dev/null`;
    if( scalar @motd < 1 ) { log_msg(3,"The security script has not executed on this system."); }
    else                   { log_msg(1,"The security script has ran on this system."); }
    return; 
} # End of the check_secure() subroutine.


#-------------------------------------------------------------------------#
# This routine checks to see if /etc/exports exists.  If so, it checks    #
# the permissions on the file to see if they are set at 600.  It also     #
# looks at the contents of the file to verify that the access option has  #
# been specified for each mount point.                                    #
#-------------------------------------------------------------------------#
sub check_exports() { 
    log_msg(1,"\nChecking the /etc/exports file.");
    if( -e "/etc/exports" ) {
        my $permissions = return_mode("/etc/exports");
        if( $permissions ne "600" ) { 
            log_msg(3,"Warning: Invalid permissions($permissions) on /etc/exports."); 
            if( $main::fix eq "1" ) { set_perms(600,"/etc/exports"); } 
        }

        open(IEXPORT,"/etc/exports");
        my @exports = <IEXPORT>;
        close(IEXPORT);

        my $counter = 1;
        foreach (@exports) {
            chomp;
            s/^\s+//;        # remove the left whitespace
            s/\s+$//;        # remove the right whitespace
            next if /^$/;    # discard blank lines
            next if /^#/;    # discard lines that begin with the comment character.
            s/\s*#.*//;      # remove partial comment lines.

            my ($fsname,$options) = split;
            if( index("$options","access") == -1 ) {
                log_msg(3,"Warning: $fsname is missing the access option.  Line $counter in /etc/exports."); }
            $counter = $counter + 1;
        }
    }
    else { log_msg(1,"The /etc/exports file does not exist."); }
    return; 
} # End of the check_exports() subroutine.


#-------------------------------------------------------------------------#
# This routine will check the permissions on the /etc/skel/* files.  If   #
# these files have the wrong permissions, every user that is added to the #
# system will have files created with the incorrect permissions.          #
#-------------------------------------------------------------------------#
sub check_skel() {
    if( -e "/etc/skel" && -d "/etc/skel" ) {
        log_msg(1,"\nChecking the permissions on the files in /etc/skel.");
        my @skfiles = `/usr/bin/find /etc/skel/ -xdev -print 2>/dev/null`;
        foreach (@skfiles) {
            chomp;
            my $permissions = return_mode($_);
            if( $permissions ne "600" ) { 
                log_msg(3,"$_ file has invalid permissions - $permissions."); 
                if( $main::fix eq "1" ) { set_perms(600,$_); }
            } 
        }
    }
    return;
} # End of the check_skel() subroutine.


#-------------------------------------------------------------------------#
# This routine checks the write permissions and the owner's uid to make   #
# sure that only root can update these files.                             #
#-------------------------------------------------------------------------#
sub check_files() { 
    log_msg(1,"\nCheck permissions on the system specific files.");
    set_special_files();
    my $filect = scalar @main::spcl_files;
    log_msg(1,"Checking $filect files on this system.");
    foreach (@main::spcl_files) {
        if( -e $_ ) {
            my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($_);
            my $perms = convert_mode($mode);
            if( substr($perms,7,1) eq "w" ) { log_msg(3,"$_ is writeable by others.  Permissions: $perms."); }
            if( $main::os_type eq "Linux" ) { 
                if( $uid >= 100 && ( ! -l $_ ) ) { 
                    my ($path,$prog) = ( $_ =~ /^(.*?)([^\/]*)$/ );
                    my $usrname = return_user($uid);
                    my $gname = return_group($gid);
                    if( $prog ne $usrname && $prog ne $gname ) { log_msg(3,"$_ is not owned by a priviledged user."); } 
                    if( $prog ne $gname && $gid > 100 && substr($perms,4,1) eq "w" ) { 
                        log_msg(3,"$_ is writeable by group members.  Permissions: $perms.");} 
                } 
            }
            else { if( $uid > 100 ) { 
                my $usrname = return_user($uid);
                log_msg(3,"$_ is not owned by a priviledged user($usrname)."); } }
        }
    }
    return; 
} # End of the check_files() subroutine.


#-------------------------------------------------------------------------#
# This routine will check for setuid files.                               #
#-------------------------------------------------------------------------#
sub check_setuid() {
    log_msg(1,"\nCheck files for setuid bit.");
    foreach (@main::spcl_files) { if( -e $_ && -u $_ ) { log_msg(1,"$_ is a setuid file."); } }
    return; 
} # End of the check_setuid() subroutine.


#-------------------------------------------------------------------------#
# This routine will check for setgid files.                               #
#-------------------------------------------------------------------------#
sub check_setgid() {
    log_msg(1,"\nCheck files for setgid bit."); 
    foreach (@main::spcl_files) { 
        if( -e $_ ) { 
            my $perms = return_perms($_);
            if( substr($perms,5,1) eq "s" ) { log_msg(1,"$_ is a setgid file."); } 
        } 
    }
    return; 
} # End of the check_setgid() subroutine.


#-------------------------------------------------------------------------#
# This routine checks the shells specified in /etc/shells, if it exists.  #
# It first checks to see if the specified shell exists on the system, and #
# then it checks the execution permissions on the shell to make sure that #
# anyone can execute the shell.                                           #
#-------------------------------------------------------------------------#
sub check_shells() {
    if( $main::os_type eq "AIX" ) { log_msg(1,"\nChecking /etc/security/login.cfg for validity."); }
    else                          { log_msg(1,"\nChecking /etc/shells for validity."); }
    
    if( scalar @main::shells > 0 ) {
        foreach (@main::shells) {
            if( -e "$_" ) {
                my $perms = return_perms($_);
                if( substr($perms,2,1) ne "x") { log_msg(3,"The shell $_ is not executable by the owner."); }
                if( -u $_ )                    { log_msg(3,"The shell $_ is a setuid executable."); }
                if( substr($perms,5,1) ne "x") { log_msg(3,"The shell $_ is not executable by the group."); }
                if( substr($perms,8,1) ne "x") { log_msg(3,"The shell $_ is not executable by others."); }
            }
            else { log_msg(3,"The shell $_ does not exist."); }
        }
    } 
    return;
} # End of the check_shells() subroutine.


#-------------------------------------------------------------------------#
# If you add any OS specific checks and/or a set of checks for a new OS,  #
# you will need to update this as well.                                   #
#-------------------------------------------------------------------------#
sub check_system() { 
    log_msg(1,"\nCheck operating system specific files.");
    if    ( $main::os_type eq "HP-UX" ) { check_hpux(); }
    elsif ( $main::os_type eq "SunOS" ) { check_sun(); }
    elsif ( $main::os_type eq "AIX" )   { check_aix(); }
    elsif ( $main::os_type eq "Linux" ) { check_linux(); }
    else                                { log_msg(1,"This is an unknown operating system."); }
    return; 
} # End of the check_system() subroutine.


#-------------------------------------------------------------------------#
# Place any HP-UX specific checks in here.                                #
#-------------------------------------------------------------------------#
sub check_hpux() {
    log_msg(1,"This is an HP system.");
    return;
} # End of the check_hpux() subroutine.


#-------------------------------------------------------------------------#
# Place any Solaris specific checks in here.                              #
#-------------------------------------------------------------------------#
sub check_sun() {
    log_msg(1,"This is an Sun system.");
    return;
} # End of the check_sun() subroutine.


#-------------------------------------------------------------------------#
# Place any AIX specific checks in here.                                  #
#-------------------------------------------------------------------------#
sub check_aix() {
    log_msg(1,"This is an AIX system.");
    return;
} # End of the check_aix() subroutine.


#-------------------------------------------------------------------------#
# Place any Linux specific checks in here.                                #
#-------------------------------------------------------------------------#
sub check_linux() {
    log_msg(1,"This is a Linux system.");
    return;
} # End of the check_linux() subroutine.


#-------------------------------------------------------------------------#
# This routine will initialize some of the variables and process any      #
# command line arguments that have been specified.                        #
#-------------------------------------------------------------------------#
sub init_vars() {
    $main::rhosts = "";
    $main::sshkey = "";
    $main::netrc = "";
    $main::samba = "";
    $main::equiv = "";
    $main::http = "";
    $main::ftp = "";
    $main::vpo = "";
    $main::secure = "";
    $main::passwd = "";
    $main::exports = "";
    $main::files = "";
    $main::system = "";
    $main::trusted = "";
    $main::skel = "";
    $main::shells = "";
    $main::setuid = "";
    $main::setgid = "";
    $main::hourly = "";
    $main::daily = "";
    $main::fix = "";

    # Get the system name.
    $main::hostname = `uname -n`;
    chomp $main::hostname;

    # Get the OS type.
    $main::os_type = `uname -s`;
    chomp $main::os_type;

    if( scalar @main::args == 0 ) { print_help_screen(); }

    # Process the command line options.
    my $result = GetOptions ( rhosts  => \$main::rhosts,
                              netrc   => \$main::netrc,
                              samba   => \$main::samba,
                              equiv   => \$main::equiv,
                              http    => \$main::http,
                              ftp     => \$main::ftp,
                              vpo     => \$main::vpo,
                              fix     => \$main::fix,
                              secure  => \$main::secure,
                              sshkey  => \$main::sshkey,
                              passwd  => \$main::passwd,
                              exports => \$main::exports,
                              files   => \$main::files,
                              system  => \$main::system,
                              trusted => \$main::trusted,
                              skel    => \$main::skel,
                              shells  => \$main::shells,
                              setuid  => \$main::setuid,
                              setgid  => \$main::setgid,
                              hourly  => sub { toggle_hourly_options() },
                              daily   => sub { toggle_daily_options() },
                              all     => sub { toggle_all_options() },
                              help    => sub { print_help_screen() },
                            );

    if    ( $main::os_type eq "Linux" ) { $main::chmod = "/bin/chmod"; }
    else                                { $main::chmod = "/usr/bin/chmod"; }

    start_prog();

    return;
} # End of the init_vars() subroutine.


#-------------------------------------------------------------------------#
# This routine will set the special files that we are going to check the  #
# permissions on.                                                         #
#-------------------------------------------------------------------------#
# Note: Any directory placed in the $dirs variable will have a find run   #
# against it and every file from that directory down will be checked.  If #
# you only want to check the permissions on the directory, place it in    #
# the @main::spcl_files array first.                                      #
#-------------------------------------------------------------------------#
sub set_special_files() {
    my $dirs = "";
    if( $main::os_type eq "HP-UX" ) {
        @main::spcl_files = qw( / /usr/bin /usr/sbin /stand /stand/build );
        $dirs = "/etc /sbin /root /usr/lib/uucp /usr/X11R6 /var/spool/cron/crontabs /var/adm/cron";
        $dirs = $dirs . " /var/spool/cron/atjobs";
    }
    elsif( $main::os_type eq "SunOS" ) {
        @main::spcl_files = qw( / );
        $dirs = "/etc /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /usr/local/etc /usr/lib /usr/X11R6 /var";
    }
    elsif( $main::os_type eq "Linux" ) {
        @main::spcl_files = qw( / );
        $dirs = "/boot /etc /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /usr/local/etc";
        $dirs = $dirs . " /usr/lib /usr/X11R6 /lib /var";
    }
    elsif( $main::os_type eq "AIX" ) {
        @main::spcl_files = qw( / );
        $dirs = "/etc /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /usr/local/etc /usr/lib /usr/X11R6 /var";
    }
    
    my @files = `/usr/bin/find $dirs -xdev -print 2>/dev/null`; 
    chomp @files; 
    foreach (@files) { push (@main::spcl_files, "$_"); }

    # Push the valid shells from /etc/shells into the special files array.
    foreach (@main::shells) { chomp; push (@main::spcl_files, "$_"); }

    return;
} # End of the set_special_files() subroutine.


#-------------------------------------------------------------------------#
# This toggles the standard daily options that we want to run.  It also   #
# modifies the logfile to save it with the date for archival.             #
#-------------------------------------------------------------------------#
sub toggle_daily_options() {
    my $today = strftime("%Y%m%d", localtime());
    $main::logfile = "/tmp/security.log." . $today;

    $main::rhosts = "1";
    $main::netrc = "1";
    $main::samba = "1";
    $main::equiv = "1";
    $main::http = "1";
    $main::ftp = "1";
    $main::secure = "1";
    $main::passwd = "1";
    $main::exports = "1";
    $main::files = "1";
    $main::system = "1";
    $main::sshkey = "1";
    $main::trusted = "1";
    $main::skel = "1";
    $main::shells = "1";
    return;
} # End of the toggle_daily_options() subroutine.


#-------------------------------------------------------------------------#
# This routine toggles the standard hourly options that we want to run.   #
#-------------------------------------------------------------------------#
sub toggle_hourly_options() {
    $main::rhosts = "1";
    $main::netrc = "1";
    $main::samba = "1";
    $main::equiv = "1";
    $main::http = "1";
    $main::ftp = "1";
    $main::passwd = "1";
    $main::exports = "1";
    $main::sshkey = "1";
    return;
} # End of the toggle_hourly_options() subroutine.


#-------------------------------------------------------------------------#
# This routine will turn on all of the specified options when the -all    #
# command line parameter is used.                                         #
#-------------------------------------------------------------------------#
sub toggle_all_options() {
    $main::rhosts = "1";
    $main::netrc = "1";
    $main::samba = "1";
    $main::equiv = "1";
    $main::http = "1";
    $main::ftp = "1";
    $main::secure = "1";
    $main::passwd = "1";
    $main::exports = "1";
    $main::files = "1";
    $main::system = "1";
    $main::sshkey = "1";
    $main::trusted = "1";
    $main::skel = "1";
    $main::shells = "1";
#    $main::setuid = "1";
#    $main::setgid = "1";
    return;
} # End of the toggle_all_options() subroutine.


#-------------------------------------------------------------------------#
# If the program is called with the -help option, this routine will print #
# a listing of all of the various options for the program.                #
#-------------------------------------------------------------------------#
sub print_help_screen() {
    print "\n";
    print "  Usage: check_security [options] \n\n";
    print "  Option     Usage\n";
    print "  -help      Display this help screen.\n";
    print "  -all       Toggles all of the security checks to run.\n";
    print "  -hourly    Toggles the hourly security checks to run.\n";
    print "  -daily     Toggles the daily security checks to run.\n";
    print "  -vpo       Use this switch to log messages to VPO.\n";
    print "  -fix       Attempt to fix the errors that have been found.\n";
    print "  -rhosts    Check the permissions on the .rhost files.\n";
    print "  -netrc     Check the permissions on the .netrc files.\n";
    print "  -sshkey    Check the permissions on the ssh2 private key files.\n";
    print "  -secure    Check to see if the secure script has ran on this system.\n"; 
    print "  -passwd    Check the /etc/passwd file for several criteria.\n"; 
    print "  -setuid    Check the system for setuid files.\n";
    print "  -setgid    Check the system for setgid files.\n";
    print "  -trusted   Check to see if the system is executing in trusted mode.\n";
    print "             Note that this is only valid for HP-UX systems.\n"; 
    print "  -samba     Check the permissions on the smb.conf file as well as see\n";
    print "             if the smbd daemon is running.\n";
    print "  -equiv     Check to see if the /etc/hosts.equiv file exists.  If so,\n";
    print "             verify that the permissions are set to 600.\n"; 
    print "  -http      Check to see if the httpd daemon is running on the system.\n"; 
    print "  -ftp       Check to see if the tftp daemon has been configured on the system.\n"; 
    print "  -exports   Check the /etc/exports file for correct permissions and\n"; 
    print "             to see if the access parameter has been set for each of\n";
    print "             specified mount points.\n";
    print "  -skel      Check the permissions on the files in /etc/skel to see if\n";
    print "             they are set properly.\n";
    print "  -shells    This will check to see if the shells in /etc/shells are\n";
    print "             actually on the system.  If so, it will check the file\n";
    print "             permissions to make sure that it is executable by all.\n";
    print "\n";
    exit;
    return;
} # End of the print_help_screen() subroutine.


#-------------------------------------------------------------------------#
# This routine just handles the startup, by opening the log file.         #
#-------------------------------------------------------------------------#
sub start_prog() {

    # Open the logfile
    open (LOGFILE,">$main::logfile") || die("Unable To Open File: $main::logfile\n");

    # Read /etc/passwd into an array for processing.
    open (IPASSWD,"/etc/passwd") || log_msg(1,"Unable To Open File: /etc/passwd.");
    @main::passwd = <IPASSWD>;
    close (IPASSWD);

    # Read /etc/group into an array for processing.
    open (IGROUP,"/etc/group") || log_msg(1,"Unable To Open File: /etc/group.");
    @main::group = <IGROUP>;
    close (IGROUP);

    # Read the valid shells into an array for processing.
    if( $main::os_type ne "AIX" ) {
        if( -e "/etc/shells") {
            open (ISHELLS,"/etc/shells") || log_msg(1,"Unable To Open File: /etc/shells.");
            @main::shells = <ISHELLS>;
            close (ISHELLS);
            chomp @main::shells;
        }
    }
    else {
        if( -e "/etc/security/login.cfg" ) {
            open(ISHELLS,"/etc/security/login.cfg") || log_msg(1,"Unable To Open File: /etc/security/login.cfg.");
            my @seccfg = <ISHELLS>;
            close(ISHELLS);
            foreach(@seccfg) { if( m/shells \= (\S+)/ ) { @main::shells = split(",",$1); } }
        }
    }

    # Get a process listing to check for samba, httpd, etc.
    @main::pslist = `ps -ef`;

    # Get the current date and time.
    $main::time = POSIX::strftime("%m-%d-%Y @ %I:%M %p", localtime());

    return;
} # End of the start_prog() subroutine.


#-------------------------------------------------------------------------#
# This routine handles the shutdown by closing the log file.              #
#-------------------------------------------------------------------------#
sub end_prog() {
    close(LOGFILE);
    return;
} # End of the end_prog() subroutine.


#-------------------------------------------------------------------------#
# This routine will print the message to the screen as well as log it in  #
# the designated log file.  If the script is ran with the -vpo option, it #
# will also send notification to VPO.                                     #
#-------------------------------------------------------------------------#
sub log_msg($$) {
    my $level = $_[0];
    my $message = $_[1];
    my $msglevel;

    if( $level == 4 ) { $msglevel = "critical"; } 
    if( $level == 3 ) { $msglevel = "major"; } 
    if( $level == 2 ) { $msglevel = "warning"; } 
    if( $level >= 1 ) { print "$message\n"; print LOGFILE "$message\n"; }
    if( $level == 0 ) { print "$message\n"; }

    if( $main::vpo eq "1" && $level >= 2 ) {
        my $msgcmd = "opcmsg application=security object=security msg_grp=Security severity=$msglevel msg_text=\"$message\"";
        system "$msgcmd";
    }
    return;
} # End of the log_msg() subroutine.


#-------------------------------------------------------------------------#
# This routine will take a numeric UID and return the username.           #
#-------------------------------------------------------------------------#
sub return_user($) {
    my $suid = $_[0];
    foreach (@main::passwd) {
        chomp;
        my ($user,$passwd,$uid,$gid,$info,$homedir,$shell) = split /:/;
       	if( $uid == $suid ) { return $user; }
    } 
    return "No User";
} # End of the return_user() subroutine.

  
#-------------------------------------------------------------------------#
# This routine will take a numeric UID and return the group name.         #
#-------------------------------------------------------------------------#
sub return_group($) {
    my $sgid = $_[0];
    foreach (@main::group) {
        chomp;
        my ($gname,$gpasswd,$gid) = split /:/;
       	if( $gid == $sgid ) { return $gname; }
    } 
    return "No Group";
} # End of the return_group() subroutine.

  
#-------------------------------------------------------------------------#
# This routine will take a fully qualified filename and return the files  #
# permissions in numeric form, such as 644.                               #
#-------------------------------------------------------------------------#
sub return_mode($) {
    my $filename = $_[0];
    my $mode;
    if( -e $filename ) {
        my $fmode = (stat("$filename"))[2] & 07777;
        $mode = sprintf("%lo",$fmode);
    }
    else { die("Unable To Open File: $filename\n"); }
    return $mode;
} # End of the return_mode() subroutine.


#-------------------------------------------------------------------------#
# This routine will take a fully qualified filename and return the string #
# representation of that number.  For example, 644 = rw-r--r--.           #
#-------------------------------------------------------------------------#
sub return_perms($) {
    my $filename = $_[0];
    my $perms = "";

    if( -e $filename ) { my $mode = (stat("$filename"))[2]; $perms = convert_mode($mode); }
    else               { $perms = "---------"; } 
    return $perms;
} # End of the return_perms() subroutine.


#-------------------------------------------------------------------------#
# This routine takes a numeric file permission and changes it to a string #
# based file permission.                                                  #
#-------------------------------------------------------------------------#
sub convert_mode($) {
    my $fmode = $_[0];
    my $perms = "";
    my $mode = sprintf("%lo",($fmode & 07777));
    if( $mode >= 4000 ) { $mode = $mode - 4000; }
    if( $mode >= 2000 ) { $mode = $mode - 2000; }
    if( $mode >= 1000 ) { $mode = $mode - 1000; }
    if( $mode >= 400 ) { $perms = $perms . "r"; $mode = $mode - 400; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 200 ) { $perms = $perms . "w"; $mode = $mode - 200; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 100 ) { $perms = $perms . "x"; $mode = $mode - 100; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 40  ) { $perms = $perms . "r"; $mode = $mode - 40; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 20  ) { $perms = $perms . "w"; $mode = $mode - 20; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 10  ) { $perms = $perms . "x"; $mode = $mode - 10; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 4   ) { $perms = $perms . "r"; $mode = $mode - 4; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 2   ) { $perms = $perms . "w"; $mode = $mode - 2; }
    else               { $perms = $perms . "-"; }
    if( $mode >= 1   ) { $perms = $perms . "x"; $mode = $mode - 1; }
    else               { $perms = $perms . "-"; }
    return $perms;
} # End of the convert_mode() subroutine.


#-------------------------------------------------------------------------#
# This routine will set the permissions on the specified file to the mode #
# that was passed as the first argument.                                  #
#-------------------------------------------------------------------------#
sub set_perms($$) {
    my $mode = $_[0];
    my $file = $_[1];

    log_msg(1,"Setting permissions on $file to $mode.");
    system "$main::chmod $mode $file 2>/dev/null"; 

    return;
} # End of the set_perms() subroutine.

