From 594e95f052d352c4b269a39e994785e4e60ef078 Mon Sep 17 00:00:00 2001 From: mulhern Date: Tue, 3 Sep 2013 20:30:50 -0400 Subject: buck-security: Parameterize hard-coded file locations. buck-security hard-codes a number of file locations for the target system on which it operates and also its own dependencies. These hard-coded dependencies have been parameterized and a few other related changes have been made. The changes are more fully explained below. buck-security.bb: * The RDEPENDS_${PN} variable has been made more orderly in anticipation of subsequent changes. It also includes a few other perl modules required by the changes to the application. * The functionality.patch file has been removed and superseded by the take_root_dir.patch patch. * The definition of S is parameterized on BPN not PN; they are different if the package has a native option. * The install step replaces the use directives and an assignment in the buck-security script in a more general way than previously. * The recipes now allows the package to have a native version. take_root_dir.patch: * buck-security * An additional flag, sysroot, that specifies the sysroot of the filesystem that the buck-security utility inspects is added. If the sysroot can not be located the script fails gracefully. * An additional flag, no-sudo, which prevents the script from exiting if it is not run by root is added. * An additional flag, disable-checks, which accepts a comma-separated list of checks to be disabled is added. * The script checks whether there has been an error in parsing the command-line arguments and fails with a usage message if there has. * The log flag now optionally takes a log file name. * The location of the configuration file is calculated relative to the location of the main script and if it can not be found the script fails gracefully. * The various file locations specified in the buck-security configuration file are made relative to the location of the buck-security script or the sysroot as appropriate. * If a log file has been specified the log is not also printed to stdout. * The command actually executed is printed in the log. * Some checks for mutually exclusive options are added. * Output level 3 is now meaningless, so it has been removed. * Various changes have been made to the report format. * Results are sorted lexicographically and, if abspath, do not include the sysroot. * checks/*.pm files * Wherever a directory had been hard-coded it is now parameterized on the sysroot. * In some cases, a test that had previously been run as a bash test was converted to a perl test to allow better handling of results and errors. * The output parameter is no longer accepted by the check procedure since this value is global. * All check procedures now accept an output_type parameter. * The dangling URLs are removed from the help text. * checks/lib/check.pm * The CheckBash and CheckPerl functions have been adapted so that the the filepaths are not hard-coded and so that the actual command is made available to the logging component. * A parameter indicating the outcome type is accepted and passed to the exception checker. * Error output is clearly distinguished from regular output. * A failure in a test is clearly distinguished from an insecure result. * The output is no longer formatted in the check functions. * checks/lib/mkchecksum.pm * The command no longer is run on non-existent directories. * checks/lib/exceptions.pm * The exception file path is located relative to the buck-security script. * If the exceptions are pathnames, the sysroot is prepended. * Correct wildcard semantics is observed. * checks/lib/users.pm * The passwd files are located relative to the sysroot. * Reading from the password file is made more principle. * The test experiences an error if files can not be found rather than the script terminating. * Some dead code is eliminated. * conf/buck-security.conf * The checksum_dir variable is a list instead of a string for easier manipulation. * The new configuration variable sysdir is added and the default is /. * The ssh_config variable is added. * All tests are included in the checks variable. * checks/sshd.pm * The ssh config file is set in the buck-security configuration file instead of hard-coded here. * checks/nopasswd.pm * This is a duplicate of emptypasswd, so it is removed. * RDEPENDS_${PN}_class-native variable is added as some tasks make no sense when run externally. Since they will not be run, there is no point --- .../buck-security/buck-security_0.6.bb | 45 +- .../buck-security/files/functionality.patch | 38 - recipes-security/buck-security/files/pseudo.patch | 74 + .../buck-security/files/take_root_dir.patch | 1686 ++++++++++++++++++++ 4 files changed, 1797 insertions(+), 46 deletions(-) delete mode 100644 recipes-security/buck-security/files/functionality.patch create mode 100644 recipes-security/buck-security/files/pseudo.patch create mode 100644 recipes-security/buck-security/files/take_root_dir.patch (limited to 'recipes-security') diff --git a/recipes-security/buck-security/buck-security_0.6.bb b/recipes-security/buck-security/buck-security_0.6.bb index f58890f..26d553a 100644 --- a/recipes-security/buck-security/buck-security_0.6.bb +++ b/recipes-security/buck-security/buck-security_0.6.bb @@ -4,15 +4,42 @@ system. This enables you to quickly overview the security status of your Linux s SECTION = "security" LICENSE = "GPL-2.0" LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6" -RDEPENDS_${PN} = "perl perl-module-term-ansicolor perl-module-posix perl-module-getopt-long perl-module-time-localtime perl-module-data-dumper perl-module-lib net-tools coreutils gnupg pinentry" +RDEPENDS_${PN} = "coreutils \ + gnupg \ + net-tools \ + perl \ + perl-module-data-dumper \ + perl-module-file-basename \ + perl-module-file-spec \ + perl-module-getopt-long \ + perl-module-lib \ + perl-module-posix \ + perl-module-term-ansicolor \ + perl-module-time-localtime \ + pinentry \ + " -SRC_URI = "http://sourceforge.net/projects/buck-security/files/buck-security/buck-security_0.6/${PN}_${PV}.tar.gz \ - file://functionality.patch" +RDEPENDS_${PN}_class-native = "coreutils \ + net-tools \ + perl \ + perl-module-data-dumper \ + perl-module-file-basename \ + perl-module-file-spec \ + perl-module-getopt-long \ + perl-module-lib \ + perl-module-posix \ + perl-module-term-ansicolor \ + perl-module-time-localtime \ + " + +SRC_URI = "http://sourceforge.net/projects/buck-security/files/buck-security/buck-security_0.6/${BPN}_${PV}.tar.gz \ + file://take_root_dir.patch \ + " SRC_URI[md5sum] = "edbd40742853fc91ffeae5b2d9ea7bab" SRC_URI[sha256sum] = "5d5dcc58b09c3a4bd87f60f86bb62cd2b0bfd7106a474951f8f520af0042a5b7" -S = "${WORKDIR}/${PN}_${PV}" +S = "${WORKDIR}/${BPN}_${PV}" do_configure() { : @@ -26,11 +53,13 @@ do_install() { install -d ${D}${bindir}/buck cp -r ${S}/* ${D}${bindir}/buck cp -r ${S}/buck-security ${D}${bindir} - sed -i 's:use lib "checks":use lib "${bindir}/buck/checks":g' ${D}${bindir}/buck-security - sed -i 's:use lib "checks/lib":use lib "${bindir}/buck/checks/lib":g' ${D}${bindir}/buck-security - sed -i 's:use lib "lib":use lib "${bindir}/buck/lib":g' ${D}${bindir}/buck-security - sed -i 's:conf/buck-security.conf:${bindir}/buck/conf/buck-security.conf:g' ${D}${bindir}/buck-security + sed -i 's!use lib "checks"!use lib File::Spec->catfile(dirname(File::Spec->rel2abs(__FILE__)), "buck/checks")!' ${D}${bindir}/buck-security + sed -i 's!use lib "checks/lib"!use lib File::Spec->catfile(dirname(File::Spec->rel2abs(__FILE__)), "buck/checks/lib")!' ${D}${bindir}/buck-security + sed -i 's!use lib "lib"!use lib File::Spec->catfile(dirname(File::Spec->rel2abs(__FILE__)), "buck/lib")!' ${D}${bindir}/buck-security + sed -i 's!my $buck_root = "."!my $buck_root = File::Spec->catfile(dirname(File::Spec->rel2abs(__FILE__)), "buck")!' ${D}${bindir}/buck-security } FILES_${PN} = "${bindir}/*" + +BBCLASSEXTEND = "native" diff --git a/recipes-security/buck-security/files/functionality.patch b/recipes-security/buck-security/files/functionality.patch deleted file mode 100644 index 86fbeee..0000000 --- a/recipes-security/buck-security/files/functionality.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 216aed597b3f20692a9c9d74dae79fa73e36c75b Mon Sep 17 00:00:00 2001 -From: Andrei Dinu -Date: Thu, 1 Aug 2013 10:30:26 +0300 -Subject: [PATCH] added functionality - - * removed sshd from the checks. When needed it can be added - in the file. - * added saving location for checksums.gpg file - -Signed-off-by: Andrei Dinu ---- - conf/buck-security.conf | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/conf/buck-security.conf b/conf/buck-security.conf -index eb88982..f573aa8 100644 ---- a/conf/buck-security.conf -+++ b/conf/buck-security.conf -@@ -27,7 +27,6 @@ usermask - superusers - services - firewall --sshd - packages_problematic - checksum - ); -@@ -48,7 +47,7 @@ $checksum_program = "sha256sum"; - $checksum_dir = "/sbin/* /bin/* /usr/sbin/* /usr/bin/*"; - - --$checksum_file = "checksums.gpg"; -+$checksum_file = "/usr/bin/checksums.gpg"; - - - --- -1.7.9.5 - diff --git a/recipes-security/buck-security/files/pseudo.patch b/recipes-security/buck-security/files/pseudo.patch new file mode 100644 index 0000000..906c1e2 --- /dev/null +++ b/recipes-security/buck-security/files/pseudo.patch @@ -0,0 +1,74 @@ +Index: buck-security_0.6/buck-security +=================================================================== +--- buck-security_0.6.orig/buck-security 2013-09-10 20:58:49.564292001 -0400 ++++ buck-security_0.6/buck-security 2013-09-11 10:07:40.440543001 -0400 +@@ -28,11 +28,6 @@ + # start time + my $start_time = time(); + +-# Runned by root? +-if ( $> != 0 ) { +- print STDERR "Only root can run this program\n"; +- exit 1; +-} + + # Read Options from command line (--help, --log and --output), run buck --help for more information + my $man = 0; +@@ -41,13 +36,15 @@ + my $opt_log = ''; # Log? via --log + my $opt_checksum = 0; + my $opt_sysroot = ''; ++my $opt_pseudo = 0; + GetOptions( + 'output=i' => \$opt_output, + 'log=s' => \$opt_log, + 'help|?' => \$help, + man => \$man, + 'make-checksums' => \$opt_checksum, +- 'sysroot=s' => \$opt_sysroot ++ 'sysroot=s' => \$opt_sysroot, ++ 'pseudo' => \$opt_pseudo + ) || + pod2usage( + -message => "\n", +@@ -73,6 +70,12 @@ + + { package Config; do $config_file } + ++if ( $> != 0 && ! $opt_pseudo) { ++ print STDERR "If you are not using pseudo you must run this script as root.\n"; ++ exit 1; ++} ++ ++ + if ($opt_sysroot) { + $Config::sysroot = $opt_sysroot; + } +@@ -98,6 +101,17 @@ + + + ++if ($opt_pseudo) { ++ @Config::checks = grep ++ { $_ ne 'checksum' && ++ $_ ne 'firewall' && ++ $_ ne 'services' && ++ $_ ne 'usermask' } ++ @Config::checks; ++} ++ ++ ++ + if ($opt_checksum != 0) { + use mkchecksum; + mkchecksum->MkChecksum(); +@@ -219,6 +233,9 @@ + --sysroot= + specify the root directory of the filesystem to be analyzed + ++ --pseudo ++ if set, run with pseudo ++ + + + =head1 FURTHER INFORMATION diff --git a/recipes-security/buck-security/files/take_root_dir.patch b/recipes-security/buck-security/files/take_root_dir.patch new file mode 100644 index 0000000..c4c3a32 --- /dev/null +++ b/recipes-security/buck-security/files/take_root_dir.patch @@ -0,0 +1,1686 @@ +Index: buck-security_0.6/buck-security +=================================================================== +--- buck-security_0.6.orig/buck-security 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/buck-security 2013-09-17 21:16:30.266513001 -0400 +@@ -4,6 +4,8 @@ + use warnings; + + # Perl Core Modules ++use File::Basename; ++use File::Spec; + use Term::ANSIColor; # for colours + use POSIX qw(nice); # for nice + use Getopt::Long; # for command line options +@@ -13,10 +15,13 @@ + + use Data::Dumper; # for debugging only + ++ + # Buck Modules +-use lib "checks"; # include security checks +-use lib "checks/lib"; # inclue checks libraries +-use lib "lib"; # include general buck library ++use lib "checks"; ++use lib "checks/lib"; ++use lib "lib"; ++ ++my $buck_root = "."; + + # VERSION + my $VERSION = '0.6'; +@@ -24,38 +29,120 @@ + # start time + my $start_time = time(); + +-# Runned by root? +-if ( $> != 0 ) { +- print STDERR "Only root can run this program\n"; +- exit 1; +-} + +-# Read Options from command line (--help, --log and --output), run buck --help for more information + my $man = 0; + my $help = 0; +-my $opt_output = '2'; # verbose level: via --output=X 2 is default +-my $opt_log = ''; # Log? via --log ++my $opt_output = 0; # verbose level: via --output=X 2 is default ++my $opt_log = 0; # Log? via --log + my $opt_checksum = 0; ++my $opt_sysroot = ''; ++my $opt_disable_checks = ''; ++my $opt_no_sudo = 0; + GetOptions( + 'output=i' => \$opt_output, +- 'log' => \$opt_log, ++ 'log:s' => \$opt_log, + 'help|?' => \$help, + man => \$man, + 'make-checksums' => \$opt_checksum, ++ 'sysroot=s' => \$opt_sysroot, ++ 'disable-checks=s' => \$opt_disable_checks, ++ 'no-sudo' => \$opt_no_sudo ++) || ++pod2usage( ++ -message => "\n", ++ -exitval => 1, ++ -verbose => 1 + ); + ++ + # Output Help if needed via pod2usage + pod2usage( + -message => "\n", + -exitval => 0, + -verbose => 1 + ) if $help; +-pod2usage( -exitstatus => 0, -verbose => 2 ) if $man; ++ ++pod2usage( -exitval => 0, ++ -verbose => 2 ++) if $man; ++ ++pod2usage( -exitval => 1, ++ -verbose => 2, ++ -message => "Can not log or check if generating initial checksums for files.\n" ++) if $opt_checksum && ( $opt_log || $opt_disable_checks ); ++ ++if ( ! $opt_no_sudo && $> != 0 ) { ++ print STDERR "Specify -no-sudo if you wish to run not as root.\n"; ++ exit 1; ++} + + # Read Config file +-my $config_file = "conf/buck-security.conf"; ++my $config_file = File::Spec->catfile($buck_root, "conf/buck-security.conf"); ++if ( ! -f $config_file ) { ++ print STDERR "The configuration file $config_file was not found.\n"; ++ exit 1; ++} ++ + { package Config; do $config_file } + ++if ( $opt_output != 0 ) { ++ if ( grep { $_ == $opt_output } (1, 2) ) { ++ no warnings; ++ $Config::output = $opt_output; ++ } ++ else { ++ print STDERR "Unknown output format $opt_output specified.\n"; ++ exit 1; ++ } ++} ++ ++if ( $opt_sysroot ) { ++ $Config::sysroot = $opt_sysroot; ++} ++$Config::sysroot = File::Spec->rel2abs( $Config::sysroot ); ++if ( ! -d $Config::sysroot ) { ++ print STDERR "The specified sysroot $Config::sysroot does not exist.\n"; ++ exit 1; ++} ++ ++$Config::buck_root = $buck_root; ++ ++my %disabled_checks = map { $_ => 1 } split( /,/, $opt_disable_checks ); ++@Config::checks = grep( ! defined $disabled_checks { $_ }, @Config::checks); ++ ++$Config::ssh_config = ++ File::Spec->catfile( $Config::sysroot, $Config::ssh_config ); ++ ++$Config::checksum_file = ++ File::Spec->catfile( $Config::buck_root, $Config::checksum_file ); ++@Config::checksum_dir = ++ map {File::Spec->catfile( $Config::sysroot, $_ )} @Config::checksum_dir; ++ ++$Config::logdir = ++ File::Spec->catdir( $Config::buck_root, $Config::logdir ); ++ ++ ++# The -log flag has been set but no log file has been specified. ++if ( $opt_log eq '' ) { ++ ++ if ( ! -d $Config::logdir ) { ++ print STDERR "The specified log directory $Config::logdir does not exist.\n"; ++ exit 1; ++ } ++ ++ # Get Date for filename of log ++ my $year = localtime->year() + 1900; ++ my $day = sprintf( "%02d", localtime->mday() ); ++ my $month = localtime->mon() + 1; ++ $month = sprintf( "%02d", $month ); ++ my $hour = sprintf( "%02d", localtime->hour() ); ++ my $min = sprintf( "%02d", localtime->min() ); ++ my $sec = sprintf( "%02d", localtime->sec() ); ++ my $date = $day . $month . $year . "_$hour-$min-$sec"; ++ ++ $opt_log = File::Spec->catfile( $Config::logdir, "buck$date.log" ); ++} ++ + if ($opt_checksum != 0) { + use mkchecksum; + mkchecksum->MkChecksum(); +@@ -73,16 +160,18 @@ + print + "\nWe will run $nr_of_checks security checks now.\nThis may take a while...\n"; + ++ ++ ++ + my $log; # String which the Log is written to, later written to fike + + # sub which decides if a log should be written (if --log was given) or not, used instead of print + sub logprint { + my $string = shift; # string given to sub + +- #print and log ++ #log only + if ($opt_log) { + $log .= $string; # add string to log string +- print $string; + } + + #print only +@@ -99,23 +188,40 @@ + my $pack = $_; # Package-Name + my $mod = $pack . '.pm'; # Module Name + require $mod; +- my ( $title, $result, $help_msg, $details ) = +- $pack->check($opt_output); # run check sub in check module +- $title = sprintf( "%-45s", "[*] CHECK $nr: $title" ); +- logprint("\n\n$title"); # print title of check +- if ( $result ne 0 ) { # if something found print [WARNING] ++ my ( $title, $result, $test, $help_msg, $details_ref, $outcome_type) = ++ $pack->check(); # run check sub in check module ++ my $first_line = sprintf( "%-60s", "[$nr] CHECK $pack: $title " ); ++ logprint("\n\n$first_line"); # print title of check ++ if ( $result != 0 ) { # if something found print [WARNING] + print color 'bold red'; + logprint("[ WARNING ]\n"); + print color 'reset'; # normal +- if ($details) { ++ my @details = @ { $details_ref }; ++ my $details; ++ if ( $result == 2 ) { ++ logprint("The security test encountered the following error during execution.\n"); ++ $details = join( "", @details ); ++ } ++ else { ++ logprint("The security test discovered a possible insecurity.\n"); + logprint("$help_msg$line$line\n"); +- logprint("$details\n"); ++ @details = sort( @details ); ++ if ( $outcome_type eq "abspath" ) { ++ @details = map { substr( $_, length( $Config::sysroot ) ) } @details; ++ if ( $Config::output > 1 ) { ++ logprint("Pathnames are relative to $Config::sysroot.\n"); ++ } ++ } ++ $details = join( "\n", @details ); + } ++ logprint("$details\n") if ( $Config::output > 1 ); + } + else { # if nothing found print [OK] + print color 'bold green'; + logprint("[ OK ]\n"); ++ print color 'reset'; + } ++ logprint("Command was: $test\n"); + print color 'bold blue'; + + $nr++; +@@ -135,22 +241,10 @@ + + print color 'reset'; + +-# LOGGING +-if ($opt_log) { + +- # Get Date for filename of log +- my $year = localtime->year() + 1900; +- my $day = sprintf( "%02d", localtime->mday() ); +- my $month = localtime->mon() + 1; +- $month = sprintf( "%02d", $month ); +- my $hour = sprintf( "%02d", localtime->hour() ); +- my $min = sprintf( "%02d", localtime->min() ); +- my $sec = sprintf( "%02d", localtime->sec() ); +- my $date = $day . $month . $year . "_$hour-$min-$sec"; + +- # Write Log string to file +- my $logfile = "logs/buck$date.log"; +- open my $LOG, '>', $logfile or die $!; ++if ($opt_log) { ++ open my $LOG, '>', $opt_log or die $!; + print $LOG $log; + close($LOG); + } +@@ -175,8 +269,9 @@ + create checksums of important programs to recheck them with buck-security + later + +- --log +- logs output in logs-directory ++ --log= ++ log output to specified file. If flag is set but no file is specified the ++ log file path is auto-generated. + + --output=1 + short output, show result only +@@ -184,8 +279,14 @@ + --output=2 (default) + default output, show details (which files/dirs where found f.e.) + +- --output=3 +- long output, also show errors ++ --sysroot= ++ specify the root directory of the filesystem to be analyzed ++ ++ --disable-checks= ++ a comma separated list of checks to disable ++ ++ --no-sudo ++ do not require the user running the script to be super + + + +Index: buck-security_0.6/checks/worldwriteablefiles.pm +=================================================================== +--- buck-security_0.6.orig/checks/worldwriteablefiles.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/worldwriteablefiles.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -2,24 +2,43 @@ + + use strict; + use warnings; ++ ++use File::Spec; ++ + use lib::check; # for the "real" check Sub + + package worldwriteablefiles; + +-my $title = "World Writeable Files"; # title of the test +-my $security_test = "find / ! -fstype proc -type f -perm -2 2> /dev/null"; # the security test +-my $exception_file = "conf/whitelists/worldwriteablefiles-whitelist.conf"; # the file with exceptions ++sub check { ++ use lib::check; + +-# help - information about the check +-my $help = <catfile( $Config::sysroot, "*" ) ); ++ if ( ! @directories ) { ++ return ( 0, @directories ); ++ } ++ ++ my $command = "find " . join( " ", @directories ) . " -perm -2 -type f"; ++ ++ @directories = `$command`; ++ chomp( @directories ); ++ ++ return ( 0, @directories ); + } + + 1; +Index: buck-security_0.6/checks/lib/check.pm +=================================================================== +--- buck-security_0.6.orig/checks/lib/check.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/lib/check.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -8,96 +8,76 @@ + + # check.pm + # executes Bash-Oneliners +-# Gets Title and command, returns Title, Result (0 or 1 where 0 is good) , and details ++# Gets Title and command, returns Title, Result (0 or 1 where 0 is good) , the test that was run, the help text, the outcomes, and the errors + + sub CheckBash { +- my $result = 0; + my $title = shift; +- my $security_test = shift; ++ my $security_test_ref = shift; ++ my $outcome_type = shift; + my $exception_file = shift; + my $help = shift; +- my $opt_output = shift; # output errors? +- my @alarms; + my @outcomes; +- my $outcome; # string to return +- # be verbose +- if ( $opt_output == '3' ) { +- @alarms = `$security_test`; # execute test and save outcome WITH errors +- chomp(@alarms); +- } +- +- # no Errors if --output=1 or 2 (for 1 see below) +- elsif ( $opt_output == '1' || $opt_output == '2' ) { +- @alarms = `$security_test 2> /dev/null`; # execute test and save outcome WITHOUT errors +- chomp(@alarms); +- } +- +- # nothing found, return 0 and exit +- if ( @alarms eq "" ) { +- return ( $title, $result, 0 ); # last 0 is for $details ++ my $security_test = $security_test_ref->(); ++ ++ my @errors = `$security_test 3>&1 1>/dev/null 2>&3`; ++ ++ if ( @errors ) { ++ return ( $title, 2, $security_test, $help, \@errors ); + } + ++ @outcomes = `$security_test 2> /dev/null`; # execute test and save outcome WITHOUT errors ++ chomp(@outcomes); ++ + # Now check outcome against exceptions +- @outcomes = exceptions::CheckAgainstExceptions(\@alarms, $exception_file); +- # if nothing left, return 0 and exit +- if ( @outcomes eq 0 ) { +- return ( $title, $result, 0 ); # second 0 is for $details +- exit; ++ @outcomes = exceptions::CheckAgainstExceptions(\@outcomes, ++ $exception_file, ++ $outcome_type); ++ ++ if ( ! @outcomes ) { ++ return ( $title, 0, $security_test ); + } + + # found something which wasn't in the exceptions from config, return it + else { +- $outcome = join( "\n", @outcomes ); # translate back to string +- $result = 1; +- +- # supress Details when --output=1 +- if ( $opt_output == '1' ) { +- $outcome = 0; +- } +- +- return ( $title, $result, $help, $outcome ); ++ return ( $title, 1, $security_test, $help, \@outcomes, $outcome_type ); + } + } + + sub CheckPerl { + my $title = shift; + my $package_name = shift; ++ my $outcome_type = shift; + my $exception_file = shift; + my $help = shift; +- my $opt_output = shift; + my @outcomes; +- my $result = 0; + my $mod = $package_name . '.pm'; ++ my $security_test = "a perl script, too long to display"; ++ + # excute the check in file at /checks, @outcomes is defined there + require $mod; +- my $outcome; # string to return + +- my @alarms = $package_name->perl(); +- # nothing found, return 0 and exit +- if ( @alarms eq "" ) { +- return ( $title, $result, 0 ); # last 0 is for $details ++ my ( $result, @details ) = $package_name->perl(); ++ ++ if ( $result == 1 ) { ++ return ( $title, 2, $security_test, $help, \@details ); ++ } ++ if ( ! @details ) { ++ return ( $title, 0, $security_test ); + } + + # Now check outcome against exceptions +- @outcomes = exceptions::CheckAgainstExceptions(\@alarms,$exception_file); ++ @outcomes = exceptions::CheckAgainstExceptions(\@details, ++ $exception_file, ++ $outcome_type); + + # if nothing left, return 0 and exit +- if ( @outcomes eq 0 ) { +- return ( $title, $result, 0 ); # second 0 is for $details +- exit; ++ if ( ! @outcomes ) { ++ return ( $title, 0, $security_test ); + } + + # found something which wasn't in the exceptions from config, return it + else { +- my $outcome = join( "\n", @outcomes ); # translate back to string +- $result = 1; +- +- # supress Details when --output=1 +- if ( $opt_output == '1' ) { +- $outcome = 0; +- } +- +- return ( $title, $result, $help, $outcome ); ++ return ( $title, 1, $security_test, $help, \@outcomes, $outcome_type ); + } + + } +Index: buck-security_0.6/checks/worldwriteabledirs.pm +=================================================================== +--- buck-security_0.6.orig/checks/worldwriteabledirs.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/worldwriteabledirs.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -2,25 +2,43 @@ + + use strict; + use warnings; ++ ++use File::Spec; ++ + use lib::check; # for the "real" check Sub + + package worldwriteabledirs; + +-my $title = "World Writeable Directories"; # title of the test +-my $security_test = +- "find / -type d -perm -o+w 2> /dev/null"; # the security test +-my $exception_file = "conf/whitelists/worldwriteabledirs-whitelist.conf"; # the file with exceptions ++sub check { + +-# help - information about the check +-my $help = <catfile( $Config::sysroot, "*" ) ); ++ if ( ! @directories ) { ++ return ( 0, @directories ); ++ } ++ ++ my $command = "find " . join( " ", @directories ) . " -perm -o+w -type d"; ++ ++ @directories = `$command`; ++ chomp( @directories ); ++ ++ return ( 0, @directories ); + } + + 1; +Index: buck-security_0.6/checks/stickytmp.pm +=================================================================== +--- buck-security_0.6.orig/checks/stickytmp.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/stickytmp.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -2,28 +2,56 @@ + + use strict; + use warnings; ++use File::Spec; + use lib::check; # for the "real" check Sub + + package stickytmp; + +-my $title = "Sticky-Bit set for /tmp"; # title of the test +-my $security_test = +- 'ls -ld /tmp | awk \'{print $1":"$3":"$4}\''; # the security test +-my $exception_file = +- "conf/whitelists/stickytmp-whitelist.conf"; # the file with exceptions +-# help - information about the check +-my $help = <catdir($Config::sysroot, 'tmp'); ++ ++ if ( ! -d $tmpdir ) { ++ return ( 1, @ { ["Specified tmpdir $tmpdir does not exist."] } ); ++ } ++ ++ my $command = 'ls -ld ' . $tmpdir; ++ my $ls_pattern = '^([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)'; ++ my @outcomes = `$command`; ++ chomp( @outcomes ); ++ if ( @outcomes != 1) { ++ return ( 1, @ { ["ls -ld result has multiple lines."] } ); ++ } ++ else { ++ my $info = $outcomes[0]; ++ if ( $info =~ m/$ls_pattern/ ) { ++ return ( 0, @ { ["$1:$3:$4"] } ); ++ } ++ else { ++ return ( 1, @ { ["Format of ls -ld result $info is unexpected."] } ); ++ } ++ } + } + + 1; +Index: buck-security_0.6/checks/suids.pm +=================================================================== +--- buck-security_0.6.orig/checks/suids.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/suids.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -2,27 +2,46 @@ + + use strict; + use warnings; ++ ++use File::Spec; ++ + use lib::check; # for the "real" check Sub + + package suids; + +-my $title = "Files where Setuid is used"; # title of the test +-my $security_test = "find / -perm -4000 -type f"; # the security test +-my $exception_file = +- "conf/whitelists/suids-whitelist.conf"; # the whitelist file +- +-# help - information about the check +-my $help = <catfile( $Config::sysroot, "*" ) ); ++ if ( ! @directories ) { ++ return ( 0, @directories ); ++ } ++ ++ my $command = "find " . join( " ", @directories ) . " -perm -4000 -type f"; ++ ++ @directories = `$command`; ++ chomp( @directories ); + +-# just forwarding to the "real" check Sub with variables +-sub check () { +- my $opt_output = $_[1]; +- check::CheckBash( $title, $security_test, $exception_file, $help, $opt_output ); ++ return ( 0, @directories ); + } + + 1; +Index: buck-security_0.6/checks/sgids.pm +=================================================================== +--- buck-security_0.6.orig/checks/sgids.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/sgids.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -2,28 +2,46 @@ + + use strict; + use warnings; ++ ++use File::Spec; ++ + use lib::check; # for the "real" check Sub + + package sgids; + +-my $title = "Files where Setgid is used"; # title of the test +-my $security_test = "find / -perm -2000 -type f"; # the security test +-my $exception_file = +- "conf/whitelists/sgids-whitelist.conf"; # the file with exceptions +- +-# help - information about the check +-my $help = <catfile( $Config::sysroot, "*" ) ); ++ if ( ! @directories ) { ++ return ( 0, @directories ); ++ } ++ ++ my $command = "find " . join( " ", @directories ) . " -perm -2000 -type f"; ++ ++ @directories = `$command`; ++ chomp( @directories ); ++ ++ return ( 0, @directories ); + } + + 1; +Index: buck-security_0.6/checks/usermask.pm +=================================================================== +--- buck-security_0.6.orig/checks/usermask.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/usermask.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -8,22 +8,28 @@ + + my $title = + "Check umask"; # title of the test +-my $security_test = 'umask'; # the security test ++sub security_test { ++ return 'umask'; ++} + my $exception_file = + "conf/whitelists/usermask-whitelist.conf"; # the file with exceptions + ++my $outcome_type = "other"; ++ + # help - information about the check + my $help = <; ++ ++ my %UsersPasswdNormal = users::PasswordsNormal(@PasswdFile); ++ ++ while ( my ($k,$v) = each %UsersPasswdNormal ) { ++ if ( $v->{'userid'} =~ '00*' || $v->{'groupid'} =~ '00*' ) { ++ push(@outcomes, $k); ++ } ++ } ++ return ( 0, @outcomes ); + } + + 1; +Index: buck-security_0.6/lib/mkchecksum.pm +=================================================================== +--- buck-security_0.6.orig/lib/mkchecksum.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/lib/mkchecksum.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -9,8 +9,11 @@ + + # MkChecksum + sub MkChecksum { ++ my @checksum_dirs = grep( -d $_ , @Config::checksum_dir); + # Create checksums +- `$Config::checksum_program $Config::checksum_dir | gpg -c > $Config::checksum_file; chmod 600 $Config::checksum_file;`; ++ if (@checksum_dirs) { ++ `find @checksum_dirs -type f | xargs $Config::checksum_program | gpg -c > $Config::checksum_file; chmod 600 $Config::checksum_file;`; ++ } + } + + 1; +Index: buck-security_0.6/checks/lib/exceptions.pm +=================================================================== +--- buck-security_0.6.orig/checks/lib/exceptions.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/lib/exceptions.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -3,6 +3,8 @@ + use strict; + use warnings; + ++use File::Spec; ++ + package exceptions; + + # exceptions.pm +@@ -14,6 +16,7 @@ + + sub GetExceptions { + my $config_file = shift; ++ my $outcome_type = shift; + open( CONFIG, "<", $config_file ) + or die "Couldn't read $config_file: $!\n"; + +@@ -26,6 +29,11 @@ + chomp; + s/#.*//; + } ++ ++ if ( $outcome_type eq "abspath" && $Config::sysroot ne "/" ) { ++ @exceptions = map { File::Spec->catfile( $Config::sysroot, substr ( $_, 1) ) } @exceptions; ++ } ++ + return @exceptions; + } + +@@ -39,54 +47,31 @@ + my $alarms_ref = shift; + my @alarms = @{$alarms_ref}; + my $exception_file = shift; +- my @outcomes; ++ my $outcome_type = shift; + # EXCEPTION PROCESSING + # get exceptions and alarms and compare: @outcomes = alarms which are no exceptions + # Code found at http://www.perlmonks.org/?node_id=2461 + ++ $exception_file = File::Spec->catfile( $Config::buck_root, $exception_file ); ++ + # only if exception file exists + if (-e $exception_file) { +- my @all_exceptions = GetExceptions($exception_file); +- # get exceptions with wildcard +- my @wildcard_exceptions = grep( /\*/, @all_exceptions ); +- # TODO: have to run grep two times to split, not very elegant +- my @normal_exceptions = grep( !/\*/, @all_exceptions ); +- +- # remove normal exceptions first +- my %normal_exceptions = map { $_ => 1 } @normal_exceptions; +- my %alarms = map { $_ => 1 } @alarms; +- @outcomes = grep( !defined $normal_exceptions{$_}, @alarms ); +- +- # Now lets see if there are wildcard exceptions (including a *) +- if (@wildcard_exceptions) { +- my %outcomes = map { $_ => 1 } +- @outcomes; # make hash out of outcomes, set all element to 1 +- +- # run through outcomes +- foreach my $hit (@outcomes) { +- +- # run through wildcard execption +- foreach my $wildcard (@wildcard_exceptions) { +- if ( $hit =~ /^$wildcard/ ) { +- +- # if wildcard matches outcome set it to 0 in hash (default is 1) +- $outcomes{$hit} = 0; +- } +- } ++ my @exceptions = GetExceptions($exception_file, $outcome_type); + ++ if ( $outcome_type eq "abspath" ) { ++ my @all_exceptions; ++ foreach my $exception (@exceptions) { ++ @all_exceptions = ( @all_exceptions, glob ( $exception ) ); ++ } ++ @exceptions = @all_exceptions; + } + +- # put all the elements still set to 1 (which means no wildcard exception had matched) in @outcomes +- @outcomes = grep { $outcomes{$_} == 1 } keys %outcomes; +- @outcomes = sort(@outcomes); # sort it +- } +- } +- # if no exception file all alarms are passed trough +- else { +- @outcomes = @alarms; ++ my %exceptions = map { $_ => 1 } @exceptions; ++ @alarms = grep( !defined $exceptions{$_}, @alarms ); + } + +- return @outcomes; ++ ++ return @alarms; + + } + +Index: buck-security_0.6/checks/lib/users.pm +=================================================================== +--- buck-security_0.6.orig/checks/lib/users.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/lib/users.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -3,63 +3,33 @@ + use strict; + use warnings; + ++use File::Spec; + + package users; + + # users.pm + # includes subs for checking user specific stuff + +-sub ReadEtcPasswd { +- +- my $passwd_file = '/etc/passwd'; +- open(PASSWD, '<', $passwd_file) +- or die "Couldn't open $passwd_file for reading: $!\n"; +- my @file = ; +- close(PASSWD); +- return @file; +-} +- +-sub ReadEtcShadow { +- +- my $passwd_file = '/etc/shadow'; +- open(PASSWD, '<', $passwd_file) +- or die "Couldn't open $passwd_file for reading: $!\n"; +- my @file = ; +- close(PASSWD); +- return @file; +-} +- +- +- +- +-sub UsersWithValidShell { +- # list of users with valid shell as array +- my @users_valid_shell; +- my @passwd_file = ReadEtcPasswd(); +- # root:x:0:0:root:/root:/bin/bash +- foreach my $line (@passwd_file) { +- $line =~ +- /(.*):(.*):(.*):(.*):(.*):(.*):(.*)/; +- # if $7 (the shell) isnt set to the following add to valid shells +- $7 ne '/bin/false' && $7 ne '/usr/sbin/nologin' && $7 ne '/bin/sync' +- ? +- push(@users_valid_shell, $1) +- : +- next; +- } +- return @users_valid_shell; +- ++sub GetPasswdFile { ++ my $filename = $_[0]; ++ return File::Spec->catfile( $Config::sysroot, "etc/$filename" ); + } + + + # get password items from /etc/passwd + sub PasswordsNormal { + my %UserPasswordNormal; +- my @passwd_file = ReadEtcPasswd(); ++ my @passwd_file = $_[0]; + foreach my $line (@passwd_file) { + $line =~ + /(.*):(.*):(.*):(.*):(.*):(.*):(.*)/; +- $UserPasswordNormal{$1} = $2; ++ $UserPasswordNormal{$1} = ++ {'password' => $2, ++ 'userid' => $3, ++ 'groupid' => $4, ++ 'info' => $5, ++ 'home' => $6, ++ 'shell' => $7}; + } + + return %UserPasswordNormal; +@@ -68,14 +38,23 @@ + # get password items from /etc/shadow + sub PasswordsShadow { + my %UserPasswordShadow; +- my @passwd_file = ReadEtcShadow(); ++ my @passwd_file = $_[0]; + foreach my $line (@passwd_file) { + $line =~ + /(.*):(.*):(.*):(.*):(.*):(.*):(.*):(.*):(.*)/; +- $UserPasswordShadow{$1} = $2; ++ $UserPasswordShadow{$1} = ++ {'password' => $2, ++ 'days-passed' => $3, ++ 'days-may' => $4, ++ 'days-must' => $5, ++ 'days-warn' => $6, ++ 'days-forgive' => $7, ++ 'days-disabled' => $8, ++ 'reserved' => $9}; + } + + return %UserPasswordShadow; ++ + } + + +@@ -95,12 +74,6 @@ + } + + +-foreach (@test) { +-# print "$_\n"; +-} +- +- +- + } + + +Index: buck-security_0.6/conf/buck-security.conf +=================================================================== +--- buck-security_0.6.orig/conf/buck-security.conf 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/conf/buck-security.conf 2013-09-17 21:16:30.266513001 -0400 +@@ -18,18 +18,19 @@ + # ACTIVATED CHECKS + ### + @checks = qw( +-worldwriteablefiles +-worldwriteabledirs ++checksum ++emptypasswd ++firewall ++packages_problematic ++services ++sgids ++sshd + stickytmp + suids +-sgids +-usermask + superusers +-services +-firewall +-sshd +-packages_problematic +-checksum ++usermask ++worldwriteablefiles ++worldwriteabledirs + ); + + +@@ -45,7 +46,12 @@ + # create checksums for the following directories (if checksum check is + # enabled) + +-$checksum_dir = "/sbin/* /bin/* /usr/sbin/* /usr/bin/*"; ++@checksum_dir = qw( ++/sbin ++/bin ++/usr/sbin ++/usr/bin ++); + + + $checksum_file = "checksums.gpg"; +@@ -74,3 +80,11 @@ + python-scapy + tshark + ); ++ ++$logdir = "logs"; ++ ++$sysdir = '/'; ++ ++$output = 2; ++ ++$ssh_config = "etc/ssh/sshd_config"; +Index: buck-security_0.6/checks/sshd.pm +=================================================================== +--- buck-security_0.6.orig/checks/sshd.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/sshd.pm 2013-09-17 21:16:30.266513001 -0400 +@@ -9,7 +9,6 @@ + # title, filename of this file, exception file and error level + sub check { + use lib::check; +- my $opt_output = $_[1]; + + # the title of the check for the output + my $title = "Check if sshd is secured"; +@@ -17,14 +16,20 @@ + my $package_name = "sshd"; + # the exception file + my $exception_file = "conf/whitelists/sshd-whitelist.conf"; ++ ++ my $outcome_type = "other"; ++ + # help - information about the check + my $help = < /dev/null | $checksums_prog -cw | grep -v ": OK";`; ++ my @outcomes = `gpg -d $checksums_file 2> /dev/null | $checksums_prog -cw | grep -v ": OK";`; ++ return ( 0, @outcomes ); + } + else { +- push(@outcomes, "Couldn't read $checksums_file: $!\n"); ++ return ( 1, @ { ["Couldn't read $checksums_file: $!\n"] } ); + } +-# +-# CHECK END +-###################################### +-return @outcomes; ++ + } + + 1; +Index: buck-security_0.6/checks/emptypasswd.pm +=================================================================== +--- buck-security_0.6.orig/checks/emptypasswd.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/emptypasswd.pm 2013-09-17 21:16:30.270513001 -0400 +@@ -9,7 +9,6 @@ + # title, filename of this file, exception file and error level + sub check { + use lib::check; +- my $opt_output = $_[1]; + + # the title of the check for the output + my $title = "Users with empty password"; +@@ -17,13 +16,19 @@ + my $package_name = "emptypasswd"; + # the exception file + my $exception_file = "conf/whitelists/emptypasswd-whitelist.conf"; ++ ++ my $outcome_type = "other"; ++ + # help - information about the check + my $help = <; ++ ++my %UsersPasswdNormal = users::PasswordsNormal(@PasswdFile); ++ ++$PasswdFile = users::GetPasswdFile('shadow'); ++ ++if (! -f $PasswdFile ) { ++ return ( 1, @ { ["Shadow password file $PasswdFile does not exist."] } ); ++} ++ ++if ( ! open (PASSWD, '<', $PasswdFile) ) { ++ return ( 1, @ { ["Shadow password file $PasswdFile can not be opened."] } ); ++} ++ ++@PasswdFile = ; + + # user and password items from /etc/shadow +-my %UsersPasswdShadow = users::PasswordsShadow(); ++my %UsersPasswdShadow = users::PasswordsShadow(@PasswdFile); + + # check /etc/passwd first + + while ( my ($k,$v) = each %UsersPasswdNormal ) { +- if ($v eq '') { ++ my $password = $v->{'password'}; ++ if ($password eq '') { + push(@UsersEmptyPasswd, $k); + } +- elsif ($v eq 'x') { ++ elsif ($password eq 'x') { + push (@ShadowUsers, $k); + } + } +@@ -67,16 +95,15 @@ + + # only check users that were shadowed in /etc/passwd + if ($k eq $_) { +- push(@UsersEmptyPasswd, $k) if $v eq ''; ++ push(@UsersEmptyPasswd, $k) if $v->{'password'} eq ''; + } + } + } + +-my @outcomes = @UsersEmptyPasswd; + # + # CHECK END + ###################################### +-return @outcomes; ++return ( 0, @UsersEmptyPasswd ); + } + + 1; +Index: buck-security_0.6/checks/firewall.pm +=================================================================== +--- buck-security_0.6.orig/checks/firewall.pm 2013-09-17 21:16:30.170513001 -0400 ++++ buck-security_0.6/checks/firewall.pm 2013-09-17 21:16:30.270513001 -0400 +@@ -9,7 +9,6 @@ + # title, filename of this file, exception file and error level + sub check { + use lib::check; +- my $opt_output = $_[1]; + + # the title of the check for the output + my $title = "Check firewall policies"; +@@ -17,13 +16,18 @@ + my $package_name = "firewall"; + # the exception file + my $exception_file = "conf/whitelists/firewall-whitelist.conf"; ++ ++ my $outcome_type = "other"; ++ + # help - information about the check + my $help = <&1 1>/dev/null 2>&3`; ++if ( @errors ) { ++ return ( 1, @errors ); ++} + + my @policies = `iptables -nL | grep policy`; + my @outcomes; +@@ -49,7 +57,7 @@ + # + # CHECK END + ###################################### +-return @outcomes; ++return ( 0, @outcomes ); + } + + 1; +Index: buck-security_0.6/checks/nopasswd.pm +=================================================================== +--- buck-security_0.6.orig/checks/nopasswd.pm 2013-09-17 21:16:30.170513001 -0400 ++++ /dev/null 1970-01-01 00:00:00.000000000 +0000 +@@ -1,80 +0,0 @@ +-#!/usr/bin/perl +- +-use strict; +-use warnings; +- +-package emptypasswd; +- +-# just forwarding to the "real" CheckPerl Sub with variables +-# title, filename of this file, exception file and error level +-sub check { +- use lib::check; +- my $opt_output = $_[1]; +- +- # the title of the check for the output +- my $title = "Users with empty password"; +- # the filename of this file +- my $package_name = "emptypasswd"; +- # the exception file +- my $exception_file = "conf/whitelists/emptypasswd-whitelist.conf"; +- # help - information about the check +- my $help = <&1 1>/dev/null 2>&3`; ++if ( @errors ) { ++ return ( 1, @errors ); ++} + + my @netstat = `netstat -luntp`; + #my @netstat = `netstat -luntp | awk '{print \$4, \$7}'`; +@@ -74,7 +83,7 @@ + # + # CHECK END + ###################################### +-return @outcomes; ++return ( 0, @outcomes ); + } + + 1; -- cgit v1.2.3-54-g00ecf