# Copyright (C) 2002 Hewlett Packard
# Copyright (C) 2005 Jay Beale
# Copyright (C) 2005 Delphi Research

# Licensed under the GNU General Public License

# This is to be pulled into the API to define tests
# currently these only work for HP-UX

require Bastille::API;
import Bastille::API;

$GLOBAL_TEST{'AccountSecurity'}{'create_securetty'} = 
    sub { 
	# location of the securetty file
	my $securetty = &getGlobal('FILE',"securetty");
	# if the securetty file exists then
	if(-e $securetty) {
	    # if a console line is found then
	    if(&B_match_line($securetty,"\^\\s\*console\\s\*\$")) {
		# don't ask the question
		return 1;
	    }
	}
	
	# otherwise ask the question.
	return 0;
    };

$GLOBAL_TEST{'AccountSecurity'}{'hidepasswords'} =
    sub {
	# location of shadow password file
	my $shadow = &getGlobal('FILE','shadow');
	# if shadowed password file exists
	if( -e $shadow){
	    # don't ask the question
	    return 1;
	}

        return &isSystemTrusted;
    };

$GLOBAL_TEST{'AccountSecurity'}{'system_auditing'} =
    sub {
        if (&isSystemTrusted) {
           my $current_auditing = &B_get_rc("AUDITING"); 
           my $current_acct = &B_get_rc("START_ACCT");   

           if(($current_auditing =~ /1/) and ($current_acct =~ /1/)){
              return $SKIPQ;
           } else {
              return $ASKQ;
           }
        } else {
            return $ASKQ;
        }
    };

sub isSystemTrusted {
        # This is the same code as  &AccountSecurity::isSystemTrusted
        my $getprdef = &getGlobal('BIN',"getprdef");
        my $definition = `$getprdef -t 2>&1`;
        if($definition =~ "System is not trusted.") {
          return 0;
        } else {
          return 1;
        }
};

$GLOBAL_TEST{'AccountSecurity'}{'single_user_password'} = 
  sub { 
    if (&GetDistro =~ "^HP-UX11.(.*)" and $1<23) {
      my $getprdef = &getGlobal('BIN',"getprdef");
      my $bootpw = `$getprdef -m bootpw 2>&1`;
      if($bootpw !~ "bootpw=YES") {
	  return $ASKQ;
      }
      else {
	  return $SKIPQ;
      }
    } else {
	# location of system default security file.
	my $defaultSecurity = &getGlobal('FILE',"security");
	# if the BOOT_AUTH variable is set to 1 in the default
	# security file then.
	if(&B_match_line($defaultSecurity, '^\s*BOOT_AUTH\s*=\s*1')) {
	    # do not ask the question
	    return $SKIPQ;
	}
	# otherwise ask the question
	return $ASKQ;
    }
  };

foreach my $param ("ABORT_LOGIN_ON_MISSING_HOMEDIR", "NOLOGIN", ) {
  $GLOBAL_TEST{'AccountSecurity'}{$param} = 
     sub { 
	my $defaultSecurity = &getGlobal('FILE',"security");
	if(&B_match_line($defaultSecurity, "^\\s*$param\\s*=\\s*1")) {
	    return $SKIPQ;
	} else {
	    return $ASKQ;
	}
     };
}



$GLOBAL_TEST{'AccountSecurity'}{'protectrhost'} = 
  sub {
	unless (&GetDistro =~ /^OSX/) {

	    # Check if rlogin, rsh, rexec are disallowed via Pluggable 
            # Authentication Modules
	    foreach $service ( 'rexec', 'rlogin', 'rsh','login','shell' ) {
                $pamFile = &getGlobal('DIR', 'pamd') . "/$service";
		if ( -e $pamFile ) {
		    if (! &B_match_line($pamFile, 
                                        'auth\s+required\s+pam_deny\.so$')) {
                        return $ASKQ;
                    }
		}
	    }

	}

	# Check if the rsh/rlogin/rexec lines have been commented out from 
        # inetd.conf, if they exist.
        $inetdConfFile = &getGlobal('FILE', 'inetd.conf');
	if ( -e $inetdConfFile ) {
            if ( &B_match_line($inetdConfFile, 'rlogind') 
                && (! &B_match_line($inetdConfFile, '^\s*#.*rlogind')) ) {
                return $ASKQ;
            }
            if ( &B_match_line($inetdConfFile, 'rexecd') 
                && (! &B_match_line($inetdConfFile, '^\s*#.*rexecd')) ) {
                return $ASKQ;
            }
            if (&B_match_line($inetdConfFile, 'rshd') 
                && (! &B_match_line($inetdConfFile, '^\s*#.*rshd')) ) {
                return $ASKQ;
            }
	}
	if ( -e &getGlobal('FILE','xinetd.conf') ) {
	    # Some systems, including Mac OS X, have an inetd.conf file even 
            # when they're xinetd-based.  Actually, you can have both inetd 
            # and xinetd running, as long as they don't overlap ports.
	    foreach $file ('exec', 'login', 'rlogin', 'shell', 'rsh') {
		if (( -e &getGlobal('DIR', 'xinetd.d') . "/$file") 
                    && (&getGlobal('DIR', 'xinetd.d') ne '')) {
		    if (! &B_match_line(&getGlobal('DIR', 'xinetd.d') . "/$file",'^\s*disable\s*=\s*yes') ) {
                        return $ASKQ;
                    }
		}
	    }
	}
        return $SKIPQ;
  };



$GLOBAL_TEST{'AccountSecurity'}{'passwdage'} = 
  sub {
        # Check if PASS_MAX_DAYS has been set to a two-digit number.
        if (! &B_match_line('/etc/login.defs', '^\s*PASS_MAX_DAYS\s+\d\d\n')) {
            return $ASKQ;
        }
        return $SKIPQ;
  };



$GLOBAL_TEST{'AccountSecurity'}{'password_strength_linux'} = 
  sub {

      return $SKIPQ;

      # This question doesn't make sense if pam.d/passwd isn't present
      my $pamd_passwd_file = &getGlobal('FILE','pamd_passwd');
      unless ( -f $pamd_passwd_file ) {
	  &B_log('WARNING',"$pamd_passwd_file is not present -- contact a Bastille developer if this is an unsupported platform.\n");
	  return $SKIPQ;
      }

	# Make sure our pam_passwdqc.so line is there
        if (! &B_match_line($pamd_passwd_file, 
            'password required\s+pam_passwdqc.so\s+min=disabled,disabled,disabled,disabled,8\s+random=0\n') ) {
            return $ASKQ;
        }
        return $SKIPQ;
  };



$GLOBAL_TEST{'AccountSecurity'}{'cronuser'} = 
  sub {
        my $cronAllow = &getGlobal('FILE', 'cron.allow');

        # Check if the file cron.allow exists.
        if ( -e $cronAllow) {
            if (open(ALLOW, "<$cronAllow")) {

                while (my $line = <ALLOW>) {
                    unless ($line =~ /^root$/) {
                        return $ASKQ;
                    }
                }
            }
            else {
                # Something is wrong if the file can't be opened.
                return $ASKQ;
            }
        }
        return $SKIPQ;
  };



$GLOBAL_TEST{'AccountSecurity'}{'umaskyn'} = 
  sub {
        # Get the required umask value.
	my $reqdumask = &getGlobalConfig('AccountSecurity', 'umask');
	my @filelist = ('profile', 'rootprofile', 'zprofile', 'csh.login');
	if (&GetDistro =~ '^OSX') {
	    @filelist = ('profile', 'csh.login');
	}

        # Check each startup file for the corect umask setting.
        for my $startupfile (@filelist) {
            my $fullpath = &getGlobal('FILE', $startupfile);
	    if ( -e $fullpath ) {
               	if (! &B_match_line($fullpath, '^\s*umask\s+' . $reqdumask)) {
                    return $ASKQ;
                }
	    }
        }

        # On HP-UX11.22 and later, check the system umask for all pam_unix(5) 
        # initiated sessions
	if (&GetDistro =~ '^HP-UX11.(.*)' and $1>20) {
	    if (defined $reqdumask) {
		my $secfile = &getGlobal('FILE', 'security');
                if (! &B_match_line($secfile, '\s*UMASK\s+' . $reqdumask)) {
                    return $ASKQ;
                }
	    }
	}
        return $SKIPQ;
  };



$GLOBAL_TEST{'AccountSecurity'}{'rootttylogins'} = 
  sub {
	my $tty;

        if ( -e '/etc/securetty' ) {
            # Check for forbidden lines.
            foreach $tty (1, 2, 3, 4, 5, 6, 7, 8, 9, 0) {
	        if (&B_match_line('/etc/securetty', "tty$tty")) {
                    return $ASKQ;
                }
                if (&B_match_line('/etc/securetty', "vc/$tty")) {
                    return $ASKQ;
                }
            }
        }
        else {
            # If the file /etc/securetty does not exist, root can log in 
            # from anywhere!
            return $ASKQ;
        }

	# Check that root cannot login in via xdm, gdm, or kdm.
        # TODO: There are more was to prevent root logins, and code should be 
        # added to check for these as well.  What is done here is just check
        # what Bastille would have done.
        if ( -e '/etc/bastille-no-login' ) {
            unless (&B_match_line('/etc/bastille-no-login', 'root') ) {
                return $ASKQ;
            }
            if ( -e '/etc/pam.d/xdm') {
                unless (&B_match_line('/etc/pam.d/xdm', 
                                      'auth\s+required\s+/lib/security/pam_listfile.so\s+onerr=succeed\s+item=user\s+sense=deny\s+file=/etc/bastille-no-login')
                                     ) {
                    return $ASKQ;
                }
            }
            if ( -e '/etc/pam.d/gdm') {
                unless (&B_match_line('/etc/pam.d/gdm', 
                                      'auth\s+required\s+/lib/security/pam_listfile.so\s+onerr=succeed\s+item=user\s+sense=deny\s+file=/etc/bastille-no-login')
                                     ) {
                    return $ASKQ;
                }
            }
            if ( -e '/etc/pam.d/kde') {
                unless (&B_match_line('/etc/pam.d/kde', 
                                      'auth\s+required\s+/lib/security/pam_listfile.so\s+onerr=succeed\s+item=user\s+sense=deny\s+file=/etc/bastille-no-login')
                                     ) {
                    return $ASKQ;
                }
            }    
        }
        return $SKIPQ;
  };



$GLOBAL_TEST{'AccountSecurity'}{'forbiduserview'} = 
  sub {
      # Get our config file locations.
      my $kdmrc = &getGlobal('FILE','kdmrc');
      my $gdmconf = &getGlobal('FILE','gdm.conf');

      # If this is an older kdmrc, it'll use a UserView line.
      if (&B_match_line($kdmrc, '^\s*UserView\s*=\s*true')) {
	  return $ASKQ;
      }
      # If this is a kdmrc that is new enough for a ShowUsers line, confirm that it is 
      # set to 'None', since this is not the default.
      if (&B_match_line($kdmrc,'ShowUsers') ) {
	  unless (&B_match_line($kdmrc,'^\s*ShowUsers\s*=\s*None')) {
	      return $ASKQ;
	  }
      }      
      # Check on gdm's Browser line.  Older gdm.conf's use 0/1, newer use True/False.
      if ( &B_match_line($gdmconf, '^\s*Browser\s*=\s*1')) {
	  return $ASKQ;
      }
      if ( &B_match_line($gdmconf, '^\s*Browser\s*=\s*true')) {
	  return $ASKQ;
      }
      return $SKIPQ;
  };

$GLOBAL_TEST{'AccountSecurity'}{'removeaccounts'} = 
    sub {
	# We need to check if specific extra accounts have been removed on specific
	# operating systems.

        # This question was written as part of the Fort Knox Linux project, where
        # the suggested deleted accounts were:
        
        # Red Hat Enterprise Linux 3: gopher, games
        # SuSE Enterprise 9: games, uucp"

	# Don't force this on any systems 
	if (&GetDistro !~ /RHEL/ and &GetDistro !~ /SESLES/) {
	    return $SKIPQ;
	}

	my @users_to_check;
	if (&GetDistro =~ /RHEL/) {
	    @users_to_check = ('gopher','games');
	}
	else {
	    @users_to_check = ('games','uucp');
	}
	# Loop through looking for illegal users
	foreach $user (@users_to_check) {
	    if (&B_match_line('/etc/passwd',"^\\s*$user\\s*:")) {
		return $ASKQ;
	    }
	}
	return $SKIPQ;
    };

$GLOBAL_TEST{'AccountSecurity'}{'removegroups'} = 
    sub {
	# We need to check if specific extra groups have been removed on specific
	# operating systems.

        # This question was written as part of the Fort Knox Linux project, where
        # the suggested deleted groups were:
        
        # SuSE Enterprise 9: games, modem, xok"

	# Don't force this on any systems 
	if (&GetDistro !~ /SESLES/) {
	    return $SKIPQ;
	}

	my @groups_to_check = ('games','modem','xok');

	# Loop through looking for illegal groups
	foreach $group (@groups_to_check) {
	    if (&B_match_line('/etc/group',"^\\s*$group\\s*:")) {
		return $ASKQ;
	    }
	}
	return $SKIPQ;
    };

1;
