aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYING.MIT17
-rw-r--r--README64
-rw-r--r--conf/layer.conf10
-rw-r--r--recipes-example/example/example-0.1/example.patch12
-rw-r--r--recipes-example/example/example-0.1/helloworld.c8
-rw-r--r--recipes-security/bastille/bastille_3.2.1.bb160
-rw-r--r--recipes-security/bastille/files/API.pm2528
-rw-r--r--recipes-security/bastille/files/AccountPermission.pm1060
-rw-r--r--recipes-security/bastille/files/Curses-and-IOLoader-changes.patch51
-rw-r--r--recipes-security/bastille/files/FileContent.pm1153
-rw-r--r--recipes-security/bastille/files/HPSpecific.pm1983
-rw-r--r--recipes-security/bastille/files/Miscellaneous.pm166
-rw-r--r--recipes-security/bastille/files/ServiceAdmin.pm690
-rwxr-xr-xrecipes-security/bastille/files/config106
-rw-r--r--recipes-security/bastille/files/fix_version_parse.patch21
-rw-r--r--recipes-security/bastille/files/yocto-standard-patch.patch72
-rw-r--r--recipes-security/checksecurity/checksecurity_2.0.14.bb16
-rw-r--r--recipes-security/curses-perl/curses-perl_1.28.bb27
-rw-r--r--recipes-security/lib-perl/lib-perl_0.63.bb27
-rw-r--r--recipes-security/pax-utils/pax-utils_0.7.bb20
-rw-r--r--recipes-security/redhat-security/files/find-chroot-py.sh96
-rw-r--r--recipes-security/redhat-security/files/find-chroot.sh93
-rw-r--r--recipes-security/redhat-security/files/find-elf4tmp.sh84
-rw-r--r--recipes-security/redhat-security/files/find-execstack.sh72
-rw-r--r--recipes-security/redhat-security/files/find-hidden-exec.sh21
-rw-r--r--recipes-security/redhat-security/files/find-nodrop-groups.sh85
-rw-r--r--recipes-security/redhat-security/files/find-sh4errors.sh132
-rw-r--r--recipes-security/redhat-security/files/find-sh4tmp.sh116
-rw-r--r--recipes-security/redhat-security/files/lib-bin-check.sh31
-rw-r--r--recipes-security/redhat-security/files/rpm-chksec.sh279
-rw-r--r--recipes-security/redhat-security/files/rpm-drop-groups.sh131
-rw-r--r--recipes-security/redhat-security/files/selinux-check-devices.sh12
-rw-r--r--recipes-security/redhat-security/files/selinux-ls-unconfined.sh19
-rw-r--r--recipes-security/redhat-security/redhat-security_1.0.bb38
34 files changed, 9400 insertions, 0 deletions
diff --git a/COPYING.MIT b/COPYING.MIT
new file mode 100644
index 0000000..89de354
--- /dev/null
+++ b/COPYING.MIT
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README b/README
new file mode 100644
index 0000000..4dd7fbf
--- /dev/null
+++ b/README
@@ -0,0 +1,64 @@
+This README file contains information on the contents of the
+security layer.
+
+Please see the corresponding sections below for details.
+
+
+Dependencies
+============
+
+This layer depends on:
+
+ URI: git://git.openembedded.org/bitbake
+ branch: master
+
+ URI: git://git.openembedded.org/openembedded-core
+ layers: meta
+ branch: master
+
+ URI: git://git.yoctoproject.org/xxxx
+ layers: xxxx
+ branch: master
+
+
+Patches
+=======
+
+Please submit any patches against the security layer to the
+xxxx mailing list (xxxx@zzzz.org) and cc: the maintainer:
+
+Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com>
+
+
+Table of Contents
+=================
+
+ I. Adding the security layer to your build
+ II. Misc
+
+
+I. Adding the security layer to your build
+=================================================
+
+--- replace with specific instructions for the security layer ---
+
+In order to use this layer, you need to make the build system aware of
+it.
+
+Assuming the security layer exists at the top-level of your
+yocto build tree, you can add it to the build system by adding the
+location of the security layer to bblayers.conf, along with any
+other layers needed. e.g.:
+
+ BBLAYERS ?= " \
+ /path/to/yocto/meta \
+ /path/to/yocto/meta-yocto \
+ /path/to/yocto/meta-yocto-bsp \
+ /path/to/yocto/meta-security \
+ "
+
+
+II. Misc
+========
+
+--- replace with specific information about the security layer ---
diff --git a/conf/layer.conf b/conf/layer.conf
new file mode 100644
index 0000000..063268d
--- /dev/null
+++ b/conf/layer.conf
@@ -0,0 +1,10 @@
+# We have a conf and classes directory, add to BBPATH
+BBPATH .= ":${LAYERDIR}"
+
+# We have recipes-* directories, add to BBFILES
+BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
+ ${LAYERDIR}/recipes-*/*/*.bbappend"
+
+BBFILE_COLLECTIONS += "security"
+BBFILE_PATTERN_security = "^${LAYERDIR}/"
+BBFILE_PRIORITY_security = "6"
diff --git a/recipes-example/example/example-0.1/example.patch b/recipes-example/example/example-0.1/example.patch
new file mode 100644
index 0000000..2000a34
--- /dev/null
+++ b/recipes-example/example/example-0.1/example.patch
@@ -0,0 +1,12 @@
+#
+# This is a non-functional placeholder file, here for example purposes
+# only.
+#
+# If you had a patch for your recipe, you'd put it in this directory
+# and reference it from your recipe's SRC_URI:
+#
+# SRC_URI += "file://example.patch"
+#
+# Note that you could also rename the directory containing this patch
+# to remove the version number or simply rename it 'files'. Doing so
+# allows you to use the same directory for multiple recipes.
diff --git a/recipes-example/example/example-0.1/helloworld.c b/recipes-example/example/example-0.1/helloworld.c
new file mode 100644
index 0000000..71f2e46
--- /dev/null
+++ b/recipes-example/example/example-0.1/helloworld.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("Hello World!\n");
+
+ return 0;
+}
diff --git a/recipes-security/bastille/bastille_3.2.1.bb b/recipes-security/bastille/bastille_3.2.1.bb
new file mode 100644
index 0000000..9aa0fb1
--- /dev/null
+++ b/recipes-security/bastille/bastille_3.2.1.bb
@@ -0,0 +1,160 @@
+DESCRIPTION = "Bastille Linux is a Hardening and Reporting/Auditing Program which enhances the security of a Linux box, by configuring daemons, system settings and firewalling."
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://${S}/COPYING;md5=c93c0550bd3173f4504b2cbd8991e50b"
+# Bash is needed for set +o privileged (check busybox), might also need ncurses
+RDEPENDS_${PN} = "perl bash tcl perl-module-getopt-long perl-module-text-wrap lib-perl perl-module-file-path perl-module-mime-base64 perl-module-file-find perl-module-errno perl-module-file-glob perl-module-tie-hash-namedcapture perl-module-file-copy perl-module-english perl-module-exporter perl-module-cwd curses-perl coreutils"
+PR = "r0"
+
+inherit allarch
+
+SRC_URI = "http://sourceforge.net/projects/bastille-linux/files/bastille-linux/3.2.1/Bastille-3.2.1.tar.bz2 \
+ file://AccountPermission.pm \
+ file://FileContent.pm \
+ file://HPSpecific.pm \
+ file://Miscellaneous.pm \
+ file://ServiceAdmin.pm \
+ file://config \
+ file://fix_version_parse.patch \
+ file://yocto-standard-patch.patch \
+ file://Curses-and-IOLoader-changes.patch \
+ "
+
+SRC_URI[md5sum] = "df803f7e38085aa5da79f85d0539f91b"
+SRC_URI[sha256sum] = "0ea25191b1dc1c8f91e1b6f8cb5436a3aa1e57418809ef902293448efed5021a"
+
+S = "${WORKDIR}/Bastille"
+
+#CONFFILES_${PN} += "${sysconfdir}/init.d/skeleton"
+#
+#do_compile () {
+# ${CC} ${WORKDIR}/skeleton_test.c -o ${WORKDIR}/skeleton-test
+#}
+#
+do_install () {
+# install -d ${D}${sysconfdir}/init.d
+# cat ${WORKDIR}/skeleton | \
+# sed -e 's,/etc,${sysconfdir},g' \
+# -e 's,/usr/sbin,${sbindir},g' \
+# -e 's,/var,${localstatedir},g' \
+# -e 's,/usr/bin,${bindir},g' \
+# -e 's,/usr,${prefix},g' > ${D}${sysconfdir}/init.d/skeleton
+# chmod a+x ${D}${sysconfdir}/init.d/skeleton
+
+ install -d ${D}${sbindir}
+ install -d ${D}${libdir}/perl/site_perl/Curses
+ ln -sf perl ${D}/${libdir}/perl5
+
+ install -d ${D}${libdir}/Bastille
+ install -d ${D}${libdir}/Bastille/API
+ install -d ${D}${datadir}/Bastille
+ install -d ${D}${datadir}/Bastille/OSMap
+ install -d ${D}${datadir}/Bastille/OSMap/Modules
+ install -d ${D}${datadir}/Bastille/Questions
+ install -d ${D}${datadir}/Bastille/FKL/configs/
+ install -d ${D}${localstatedir}/lock/subsys/bastille
+ install -d ${D}${localstatedir}/log/Bastille
+ install -d ${D}${sysconfdir}/Bastille
+
+ install -m 0755 AutomatedBastille ${D}${sbindir}
+ install -m 0755 BastilleBackEnd ${D}${sbindir}
+ install -m 0755 InteractiveBastille ${D}${sbindir}
+ # Questions.txt has been replaced by Modules.txt and Questions/
+ #install -m 0644 Questions.txt ${D}${datadir}/Bastille
+ install -m 0644 Modules.txt ${D}${datadir}/Bastille
+ # New Weights file(s).
+ install -m 0644 Weights.txt ${D}${datadir}/Bastille
+ # Castle graphic
+ install -m 0644 bastille.jpg ${D}${datadir}/Bastille/
+ # Javascript file
+ install -m 0644 wz_tooltip.js ${D}${datadir}/Bastille/
+ install -m 0644 Credits ${D}${datadir}/Bastille
+ install -m 0644 FKL/configs/fkl_config_redhat.cfg ${D}${datadir}/Bastille/FKL/configs/
+
+ install -m 0755 RevertBastille ${D}${sbindir}
+ install -m 0755 bin/bastille ${D}${sbindir}
+ install -m 0644 bastille-firewall ${D}${datadir}/Bastille
+ install -m 0644 bastille-firewall-reset ${D}${datadir}/Bastille
+ install -m 0644 bastille-firewall-schedule ${D}${datadir}/Bastille
+ install -m 0644 bastille-tmpdir-defense.sh ${D}${datadir}/Bastille
+ install -m 0644 bastille-tmpdir.csh ${D}${datadir}/Bastille
+ install -m 0644 bastille-tmpdir.sh ${D}${datadir}/Bastille
+ install -m 0644 bastille-firewall.cfg ${D}${datadir}/Bastille
+ install -m 0644 bastille-ipchains ${D}${datadir}/Bastille
+ install -m 0644 bastille-netfilter ${D}${datadir}/Bastille
+ install -m 0644 bastille-firewall-early.sh ${D}${datadir}/Bastille
+ install -m 0644 bastille-firewall-pre-audit.sh ${D}${datadir}/Bastille
+ install -m 0644 complete.xbm ${D}${datadir}/Bastille
+ install -m 0644 incomplete.xbm ${D}${datadir}/Bastille
+ install -m 0644 disabled.xpm ${D}${datadir}/Bastille
+ install -m 0644 ifup-local ${D}${datadir}/Bastille
+ install -m 0644 hosts.allow ${D}${datadir}/Bastille
+
+ install -m 0644 Bastille/AccountSecurity.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/Apache.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/API.pm ${D}${libdir}/Bastille
+ install -m 0644 ${WORKDIR}/AccountPermission.pm ${D}${libdir}/Bastille/API
+ install -m 0644 ${WORKDIR}/FileContent.pm ${D}${libdir}/Bastille/API
+ install -m 0644 ${WORKDIR}/HPSpecific.pm ${D}${libdir}/Bastille/API
+ install -m 0644 ${WORKDIR}/ServiceAdmin.pm ${D}${libdir}/Bastille/API
+ install -m 0644 ${WORKDIR}/Miscellaneous.pm ${D}${libdir}/Bastille/API
+ install -m 0644 Bastille/BootSecurity.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/ConfigureMiscPAM.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/DisableUserTools.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/DNS.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/FilePermissions.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/FTP.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/Firewall.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/OSX_API.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/LogAPI.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/HP_UX.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/IOLoader.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/Patches.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/Logging.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/MiscellaneousDaemons.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/PatchDownload.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/Printing.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/PSAD.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/RemoteAccess.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/SecureInetd.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/Sendmail.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/TestDriver.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/TMPDIR.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_AccountSecurity.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_Apache.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_DNS.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_FTP.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_HP_UX.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_MiscellaneousDaemons.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_Patches.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_SecureInetd.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_Sendmail.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_BootSecurity.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_DisableUserTools.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_FilePermissions.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_Logging.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/test_Printing.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille/IPFilter.pm ${D}${libdir}/Bastille
+ install -m 0644 Bastille_Curses.pm ${D}${libdir}/perl5/site_perl
+ install -m 0644 Bastille_Tk.pm ${D}${libdir}/perl5/site_perl
+ install -m 0644 Curses/Widgets.pm ${D}${libdir}/perl5/site_perl/Curses
+
+
+
+ install -m 0644 OSMap/LINUX.bastille ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/LINUX.system ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/LINUX.service ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/HP-UX.bastille ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/HP-UX.system ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/HP-UX.service ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/OSX.bastille ${D}${datadir}/Bastille/OSMap
+ install -m 0644 OSMap/OSX.system ${D}${datadir}/Bastille/OSMap
+ install -m 0644 ${WORKDIR}/config ${D}${sysconfdir}/Bastille/config
+
+ for file in `cat Modules.txt` ; do
+ install -m 0644 Questions/$file.txt ${D}${datadir}/Bastille/Questions
+ done
+
+ ln -s ${D}${sbindir}/RevertBastille ${D}${sbindir}/UndoBastille
+}
+
+FILES_${PN} += "${datadir}/Bastille ${libdir}/Bastille ${libdir}/perl* ${sysconfdir}/*"
diff --git a/recipes-security/bastille/files/API.pm b/recipes-security/bastille/files/API.pm
new file mode 100644
index 0000000..5060f52
--- /dev/null
+++ b/recipes-security/bastille/files/API.pm
@@ -0,0 +1,2528 @@
+# Copyright (C) 1999-2007 Jay Beale
+# Copyright (C) 2001-2008 Hewlett-Packard Development Company, L.P.
+# Licensed under the GNU General Public License, version 2
+
+package Bastille::API;
+
+## TO DO:
+#
+#
+# 1) Look for more places to insert error handling...
+#
+# 2) Document this module more
+#
+#
+
+
+##########################################################################
+#
+# This module forms the basis for the v1.1 API.
+#
+ ##########################################################################
+
+#
+# This module forms the initial basis for the Bastille Engine, implemented
+# presently via a Perl API for Perl modules.
+#
+# This is still under construction -- it is very usable, but not very well
+# documented, yet.
+#
+
+##########################################################################
+#
+# API Function Listing
+#
+##########################################################################
+# The routines which should be called by Bastille modules are listed here,
+# though they are better documented throughout this file.
+#
+# Distro Specific Stuff:
+#
+# &GetDistro - figures out what distro we're running, if it knows it...
+# &ConfigureForDistro - sets global variables based on the distro
+# &GetGlobal - returns hash values defined in ConfigureForDistro
+#
+# &getGlobalConfig - returns value of hash set up by ReadConfig
+#
+# Logging Specific Stuff has moved to LogAPI.pm:
+#
+# &B_log(type,msg) -- takes care of all logging
+#
+#
+# Input functions for the old input method...
+#
+# File open/close/backup functions
+#
+# &B_open * -- opens a file handle and logs the action/error (OLD WAY!)
+# &B_open_plus -- opens a pair of file handles for the old and new version
+# of a file; respects logonly flag. (NEW WAY)
+# &B_close * -- closes a file handle and logs the action/error (OLD WAY!)
+# &B_close_plus -- closes a pair of file handles opened by B_open_plus,
+# backing up one file and renaming the new file to the
+# old one's name, logging actions/errors. Respects the
+# logonly flag -- needs B_backup file. Finally, sets
+# new file's mode,uid,gid to old file's... (NEW WAY)
+# &B_backup_file - backs up a file that is being changed/deleted into the
+# $GLOBAL_BDIR{"backup"} directory.
+#
+# Non-content file modification functions
+#
+# &B_delete_file - deletes the named file, backing up a copy
+# &B_create_file - creates the named file, if it doesn't exist
+#
+# &B_symlink - create a symlink to a file, recording the revert rm
+#
+# More stuff
+#
+# &B_createdir - make a directory, if it doesn't exist, record revert rmdir
+# &B_cp - copy a file, respecting LOGONLY and revert func.
+# &B_mknod - wrap mknod with revert and logonly and prefix functionality
+#
+# &B_read_sums - reads sum.csv file and parses input into the GLOBAL_SUM hash
+# &B_write_sums - writes sum.csv file from GLOBAL_SUM hash
+# &B_check_sum($) - take a file name and compares the stored cksum with the current
+# cksum of said file
+# &B_set_sum($) - takes a file name and gets that files current cksum then sets
+# that sum in the GLOBAL_SUM hash
+# &B_revert_log - create entry in shell script, executed later by bastille -r
+# &showDisclaimer - Print the disclaimer and wait for 5 minutes for acceptance
+###########################################################################
+# Note: GLOBAL_VERBOSE
+#
+# All logging functions now check GLOBAL_VERBOSE and, if set, will print
+# all the information sent to log files to STDOUT/STDERR as well.
+#
+
+#
+# Note: GLOBAL_LOGONLY
+#
+# All Bastille API functions now check for the existence of a GLOBAL_LOGONLY
+# variable. When said variable is set, no function actually modifies the
+# system.
+#
+# Note: GLOBAL_DEBUG
+#
+# The B_log("DEBUG",...) function now checks GLOBAL_DEBUG and, if set, it will
+# print all the information to a new debug-log file. If GLOBAL_VERBOSE is
+# set it might log to STDOUT/STDERR as well (not yet implemented, pending
+# discussion). Developers should populate appropriate places with &B_log(DEBUG)
+# in order to be able to tell users to use this options and send the logs
+# for inspection and debugging.
+#
+#
+
+
+# Libraries for the Backup_file routine: Cwd and File::Path
+use Cwd;
+use Bastille::OSX_API;
+use Bastille::LogAPI;
+use File::Path;
+use File::Basename;
+
+# Export the API functions listed below for use by the modules.
+
+use Exporter;
+@ISA = qw ( Exporter );
+@EXPORT = qw(
+ setOptions GetDistro ConfigureForDistro B_log B_revert_log
+ SanitizeEnv
+ B_open B_close B_symlink StopLogging
+ B_open_plus B_close_plus
+ B_isFileinSumDB
+ B_create_file B_read_sums B_check_sum B_set_sum isSumDifferent listModifiedFiles
+ B_create_dir B_create_log_file
+ B_delete_file
+ B_cp B_place B_mknod
+ showDisclaimer
+ getSupportedOSHash
+ B_Backtick
+ B_System
+ isProcessRunning
+ checkProcsForService
+
+
+ $GLOBAL_OS $GLOBAL_ACTUAL_OS $CLI
+ $GLOBAL_LOGONLY $GLOBAL_VERBOSE $GLOBAL_DEBUG $GLOBAL_AUDITONLY $GLOBAL_AUDIT_NO_BROWSER $errorFlag
+ %GLOBAL_BIN %GLOBAL_DIR %GLOBAL_FILE
+ %GLOBAL_BDIR %GLOBAL_BFILE
+ %GLOBAL_CONFIG %GLOBAL_SUM
+
+ %GLOBAL_SERVICE %GLOBAL_SERVTYPE %GLOBAL_PROCESS %GLOBAL_RC_CONFIG
+ %GLOBAL_TEST
+
+ getGlobal setGlobal getGlobalConfig
+
+
+ B_parse_fstab
+ B_parse_mtab B_is_rpm_up_to_date
+
+ NOTSECURE_CAN_CHANGE SECURE_CANT_CHANGE
+ NOT_INSTALLED INCONSISTENT MANUAL NOTEST SECURE_CAN_CHANGE
+ STRING_NOT_DEFINED NOT_INSTALLED_NOTSECURE DONT_KNOW
+ RELEVANT_HEADERQ NOTRELEVANT_HEADERQ
+);
+
+
+
+######################################################
+###Testing Functions
+##################################################################
+
+#Define "Constants" for test functions. Note these constants sometimes get
+#interpreted as literal strings when used as hash references, so you may
+# have to use CONSTANT() to disambiguate, like below. Sorry, it was either
+# that or create even *more* global variables.
+# See TestDriver.pm for definitions, and test design doc for full explaination
+use constant {
+ NOTSECURE_CAN_CHANGE => 0,
+ SECURE_CANT_CHANGE => 1,
+ NOT_INSTALLED => 2, # (where the lack makes the system secure, eg telnet)
+ INCONSISTENT => 3,
+ MANUAL => 4,
+ NOTEST => 5,
+ SECURE_CAN_CHANGE => 6,
+ STRING_NOT_DEFINED => 7,
+ NOT_INSTALLED_NOTSECURE => 8, #(Where the missing s/w makes the system less secure eg IPFilter)
+ #Intentional duplicates follow
+ DONT_KNOW => 5,
+ RELEVANT_HEADERQ => 6,
+ NOTRELEVANT_HEADERQ => 0
+};
+
+&SanitizeEnv;
+
+# Set up some common error messages. These are independent of
+# operating system
+
+# These will allow us to line up the warnings and error messages
+my $err ="ERROR: ";
+my $spc =" ";
+my $GLOBAL_OS="None";
+my $GLOBAL_ACTUAL_OS="None";
+my %GLOBAL_SUMS=();
+my $CLI='';
+
+#OS independent Error Messages Follow, normally "bastille" script filters
+#options before interactive or Bastille runs, so this check is often redundant
+$GLOBAL_ERROR{"usage"}="\n".
+ "$spc Usage: bastille [ -b | -c | -x ] [ --os <version> ] [ -f <alternate config> ]\n".
+ "$spc bastille [ -r | --assess | --assessnobowser ]\n\n".
+ "$spc --assess : check status of system and report in browser\n".
+ "$spc --assessnobrowser : check status of system and list report locations\n".
+ "$spc -b : use a saved config file to apply changes\n".
+ "$spc directly to system\n".
+ "$spc -c : use the Curses (non-X11) TUI\n".
+ "$spc -f <alternate config>: populate answers with a different config file\n".
+ "$spc -r : revert all Bastille changes to-date\n".
+ "$spc -x : use the Perl/Tk (X11) GUI\n" .
+ "$spc --os <version> : ask all questions for the given operating system\n" .
+ "$spc version. e.g. --os RH6.0\n";
+
+# These options don't work universally, so it's best not to
+# document them here (yet). Hopefully, we'll get them
+# straightened out soon.
+#"$spc --log : log-only option\n".
+#"$spc -v : verbose mode\n".
+#"$spc --debug : debug mode\n";
+
+
+##############################################################################
+#
+# Directory structure for Bastille Linux v1.2 and up
+#
+##############################################################################
+#
+# /usr/sbin/ -- location of Bastille binaries
+# /usr/lib/Bastille -- location of Bastille modules
+# /usr/share/Bastille -- location of Bastille data files
+# /etc/Bastille -- location of Bastille config files
+#
+# /var/log/Bastille -- location of Bastille log files
+# /var/log/Bastille/revert -- directory holding all Bastille-created revert scripts
+# /var/log/Bastille/revert/backup -- directory holding the original files that
+# Bastille modifies, with permissions intact
+#
+##############################################################################
+
+##############################################################################
+#
+# Directory structure for HP-UX Bastille v2.0 and up
+#
+##############################################################################
+#
+# /opt/sec_mgmt/bastille/bin/ -- location of Bastille binaries
+# /opt/sec_mgmt/bastille/lib/ -- location of Bastille modules
+# /etc/opt/sec_mgmt/bastille/ -- location of Bastille data and config files
+#
+# /var/opt/sec_mgmt/bastille/log/ -- location of Bastille log files
+# /var/opt/sec_mgmt/bastille/revert -- directory holding all Bastille-created
+# revert scripts and save files
+#
+##############################################################################
+
+
+##############################################################################
+##############################################################################
+################## Actual functions start here... ###########################
+##############################################################################
+##############################################################################
+
+###########################################################################
+# setOptions takes six arguments, $GLOBAL_DEBUG, $GLOBAL_LOGONLY,
+# $GLOBAL_VERBOSE, $GLOBAL_AUDITONLY, $GLOBAL_AUDIT_NO_BROWSER, and GLOBAL_OS;
+###########################################################################
+sub setOptions($$$$$$) {
+ ($GLOBAL_DEBUG,$GLOBAL_LOGONLY,$GLOBAL_VERBOSE,$GLOBAL_AUDITONLY,
+ $GLOBAL_AUDIT_NO_BROWSER,$GLOBAL_OS) = @_;
+ if ($GLOBAL_AUDIT_NO_BROWSER) {
+ $GLOBAL_AUDITONLY = 1;
+ }
+ if (not(defined($GLOBAL_OS))){
+ $GLOBAL_OS="None";
+ }
+}
+###########################################################################
+#
+# SanitizeEnv load a proper environment so Bastille cannot be tricked
+# and Perl modules work correctly.
+#
+###########################################################################
+sub SanitizeEnv {
+ delete @ENV{'IFS','CDPATH','ENV','BASH_ENV'};
+ $ENV{CDPATH}=".";
+ $ENV{BASH_ENV}= "";
+ # Bin is needed here or else /usr/lib/perl5/5.005/Cwd.pm
+ # will not find `pwd`
+ # Detected while testing with -w, jfs
+ $ENV{PATH} = "/bin:/usr/bin";
+ # Giorgi, is /usr/local/bin needed? (jfs)
+}
+
+###########################################################################
+#
+# GetDistro checks to see if the target is a known distribution and reports
+# said distribution.
+#
+# This is used throughout the script, but also by ConfigureForDistro.
+#
+#
+###########################################################################
+
+sub GetDistro() {
+
+ my ($release,$distro);
+
+ # Only read files for the distro once.
+ # if the --os option was used then
+ if ($GLOBAL_OS eq "None") {
+ if ( -e "/etc/mandrake-release" ) {
+ open(MANDRAKE_RELEASE,"/etc/mandrake-release");
+ $release=<MANDRAKE_RELEASE>;
+
+ if ( ($release =~ /^Mandrake Linux release (\d+\.\d+\w*)/) or ($release =~ /^Linux Mandrake release (\d+\.\d+\w*)/) ) {
+ $distro="MN$1";
+ }
+ elsif ( $release =~ /^Mandrakelinux release (\d+\.\d+)\b/ ) {
+ $distro="MN$1";
+ }
+ else {
+ print STDERR "$err Couldn't determine Mandrake/Mandriva version! Setting to 10.1!\n";
+ $distro="MN10.1";
+ }
+
+ close(MANDRAKE_RELEASE);
+ }
+ elsif ( -e "/etc/immunix-release" ) {
+ open(IMMUNIX_RELEASE,"/etc/immunix-release");
+ $release=<IMMUNIX_RELEASE>;
+ unless ($release =~ /^Immunix Linux release (\d+\.\d+\w*)/) {
+ print STDERR "$err Couldn't determine Immunix version! Setting to 6.2!\n";
+ $distro="RH6.2";
+ }
+ else {
+ $distro="RH$1";
+ }
+ close(*IMMUNIX_RELEASE);
+ }
+ elsif ( -e '/etc/fedora-release' ) {
+ open(FEDORA_RELEASE,'/etc/fedora-release');
+ $release=<FEDORA_RELEASE>;
+ close FEDORA_RELEASE;
+ if ($release =~ /^Fedora Core release (\d+\.?\d*)/) {
+ $distro = "RHFC$1";
+ }
+ elsif ($release =~ /^Fedora release (\d+\.?\d*)/) {
+ $distro = "RHFC$1";
+ }
+ else {
+ print STDERR "$err Could not determine Fedora version! Setting to Fedora Core 8\n";
+ $distro='RHFC8';
+ }
+ }
+ elsif ( -e "/etc/redhat-release" ) {
+ open(*REDHAT_RELEASE,"/etc/redhat-release");
+ $release=<REDHAT_RELEASE>;
+ if ($release =~ /^Red Hat Linux release (\d+\.?\d*\w*)/) {
+ $distro="RH$1";
+ }
+ elsif ($release =~ /^Red Hat Linux .+ release (\d+)\.?\d*([AEW]S)/) {
+ $distro="RHEL$1$2";
+ }
+ elsif ($release =~ /^Red Hat Enterprise Linux ([AEW]S) release (\d+)/) {
+ $distro="RHEL$2$1";
+ }
+ elsif ($release =~ /^CentOS release (\d+\.\d+)/) {
+ my $version = $1;
+ if ($version =~ /^4\./) {
+ $distro='RHEL4AS';
+ }
+ elsif ($version =~ /^3\./) {
+ $distro='RHEL3AS';
+ }
+ else {
+ print STDERR "$err Could not determine CentOS version! Setting to Red Hat Enterprise 4 AS.\n";
+ $distro='RHEL4AS';
+ }
+ }
+ else {
+ # JJB/HP - Should this be B_log?
+ print STDERR "$err Couldn't determine Red Hat version! Setting to 9!\n";
+ $distro="RH9";
+ }
+ close(REDHAT_RELEASE);
+
+ }
+ elsif ( -e "/etc/debian_version" ) {
+ $stable="3.1"; #Change this when Debian stable changes
+ open(*DEBIAN_RELEASE,"/etc/debian_version");
+ $release=<DEBIAN_RELEASE>;
+ unless ($release =~ /^(\d+\.\d+\w*)/) {
+ print STDERR "$err System is not running a stable Debian GNU/Linux version. Setting to $stable.\n";
+ $distro="DB$stable";
+ }
+ else {
+ $distro="DB$1";
+ }
+ close(DEBIAN_RELEASE);
+ }
+ elsif ( -e "/etc/SuSE-release" ) {
+ open(*SUSE_RELEASE,"/etc/SuSE-release");
+ $release=<SUSE_RELEASE>;
+ if ($release =~ /^SuSE Linux (\d+\.\d+\w*)/i) {
+ $distro="SE$1";
+ }
+ elsif ($release =~ /^SUSE LINUX Enterprise Server (\d+\.?\d?\w*)/i) {
+ $distro="SESLES$1";
+ }
+ elsif ($release =~ /^SUSE Linux Enterprise Server (\d+\.?\d?\w*)/i) {
+ $distro="SESLES$1";
+ }
+ elsif ($release =~ /^openSuSE (\d+\.\d+\w*)/i) {
+ $distro="SE$1";
+ }
+ else {
+ print STDERR "$err Couldn't determine SuSE version! Setting to 10.3!\n";
+ $distro="SE10.3";
+ }
+ close(SUSE_RELEASE);
+ }
+ elsif ( -e "/etc/turbolinux-release") {
+ open(*TURBOLINUX_RELEASE,"/etc/turbolinux-release");
+ $release=<TURBOLINUX_RELEASE>;
+ unless ($release =~ /^Turbolinux Workstation (\d+\.\d+\w*)/) {
+ print STDERR "$err Couldn't determine TurboLinux version! Setting to 7.0!\n";
+ $distro="TB7.0";
+ }
+ else {
+ $distro="TB$1";
+ }
+ close(TURBOLINUX_RELEASE);
+ }
+ else {
+ # We're either on Mac OS X, HP-UX or an unsupported O/S.
+ if ( -x '/usr/bin/uname') {
+ # uname is in /usr/bin on Mac OS X and HP-UX
+ $release=`/usr/bin/uname -sr`;
+ }
+ else {
+ print STDERR "$err Could not determine operating system version!\n";
+ $distro="unknown";
+ }
+
+ # Figure out what kind of system we're on.
+ if ($release ne "") {
+ if ($release =~ /^Darwin\s+(\d+)\.(\d+)/) {
+ if ($1 == 6 ) {
+ $distro = "OSX10.2";
+ }
+ elsif ($1 == 7) {
+ $distro = "OSX10.3";
+ }
+ elsif ($1 == 8) {
+ $distro = "OSX10.3";
+ }
+ else {
+ $distro = "unknown";
+ }
+ }
+ elsif ( $release =~ /(^HP-UX)\s*B\.(\d+\.\d+)/ ) {
+ $distro="$1$2";
+ }
+ else {
+ print STDERR "$err Could not determine operating system version!\n";
+ $distro="unknown";
+ }
+ }
+ }
+
+ $GLOBAL_OS=$distro;
+ } elsif (not (defined $GLOBAL_OS)) {
+ print "ERROR: GLOBAL OS Scoping Issue\n";
+ } else {
+ $distro = $GLOBAL_OS;
+ }
+
+ return $distro;
+}
+
+###################################################################################
+# &getActualDistro; #
+# #
+# This subroutine returns the actual os version in which is running on. This #
+# os version is independent of the --os switch feed to bastille. #
+# #
+###################################################################################
+sub getActualDistro {
+ # set local variable to $GLOBAL_OS
+
+ if ($GLOBAL_ACTUAL_OS eq "None") {
+ my $os = $GLOBAL_OS;
+ # undef GLOBAL_OS so that the GetDistro routine will return
+ # the actualDistro, it might otherwise return the distro set
+ # by the --os switch.
+ $GLOBAL_OS = "None";
+ $GLOBAL_ACTUAL_OS = &GetDistro;
+ # reset the GLOBAL_OS variable
+ $GLOBAL_OS = $os;
+ }
+ return $GLOBAL_ACTUAL_OS;
+}
+# These are helper routines which used to be included inside GetDistro
+sub is_OS_supported($) {
+ my $os=$_[0];
+ my $supported=0;
+ my %supportedOSHash = &getSupportedOSHash;
+
+ foreach my $oSType (keys %supportedOSHash) {
+ foreach my $supported_os ( @{$supportedOSHash{$oSType}} ) {
+ if ( $supported_os eq $os ) {
+ $supported=1;
+ }
+ }
+ }
+
+ return $supported;
+}
+
+###############################################################################
+# getSupportedOSHash
+#
+# This subrountine returns a hash of supported OSTypes, which point to a
+# a list of supported distros. When porting to a new distro, add the
+# distro id to the hash in its appropriate list.
+###############################################################################
+sub getSupportedOSHash () {
+
+ my %osHash = ("LINUX" => [
+ "DB2.2", "DB3.0",
+ "RH6.0","RH6.1","RH6.2","RH7.0",
+ "RH7.1","RH7.2","RH7.3","RH8.0",
+ "RH9",
+ "RHEL5",
+ "RHEL4AS","RHEL4ES","RHEL4WS",
+ "RHEL3AS","RHEL3ES","RHEL3WS",
+ "RHEL2AS","RHEL2ES","RHEL2WS",
+ "RHFC1","RHFC2","RHFC3","RHFC4",
+ "RHFC5","RHFC6","RHFC7","RHFC8",
+ "MN6.0","MN6.1 ","MN7.0","MN7.1",
+ "MN7.2","MN8.0","MN8.1","MN8.2",
+ "MN10.1",
+ "SE7.2","SE7.3", "SE8.0","SE8.1","SE9.0","SE9.1",
+ "SE9.2","SE9.3","SE10.0","SE10.1","SE10.2","SE10.3",
+ "SESLES8","SESLES9","SESLES10",
+ "TB7.0"
+ ],
+
+ "HP-UX" => [
+ "HP-UX11.00","HP-UX11.11",
+ "HP-UX11.22", "HP-UX11.23",
+ "HP-UX11.31"
+ ],
+
+ "OSX" => [
+ 'OSX10.2','OSX10.3','OSX10.4'
+ ]
+ );
+
+ return %osHash;
+
+}
+
+
+###############################################################################
+# setFileLocations(OSMapFile, currentDistro);
+#
+# Given a file map location this subroutine will create the GLOBAL_*
+# hash entries specified within this file.
+###############################################################################
+sub setFileLocations($$) {
+
+ my ($fileInfoFile,$currentDistro) = @_;
+
+ # define a mapping from the first argument to the proper hash
+ my %map = ("BIN" => \%GLOBAL_BIN,
+ "FILE" => \%GLOBAL_FILE,
+ "BFILE" => \%GLOBAL_BFILE,
+ "DIR" => \%GLOBAL_DIR,
+ "BDIR" => \%GLOBAL_BDIR
+ );
+ my @fileInfo = ();
+
+ # File containing file location information
+ if(open(FILEINFO, "<$fileInfoFile" )) {
+
+ @fileInfo = <FILEINFO>;
+
+ close(FILEINFO);
+
+ }
+ else {
+ print STDERR "$err Unable to find file location information for '$distro'.\n" .
+ "$spc Contact the Bastille support list for details.\n";
+ exit(1);
+ }
+
+ # Each line of the file map follows the pattern below:
+ # bdir,init.d,'/etc/rc.d/init.d',RH7.2,RH7.3
+ # if the distro information is not available, e.g.
+ # bdir,init.d,'/etc/rc.d/init.d'
+ # then the line applies to all distros under the OSType
+ foreach my $file (@fileInfo) {
+ # Perl comments are allowed within the file but only entire line comments
+ if($file !~ /^\s+\#|^\s+$/) {
+ chomp $file;
+ # type relates to the map above, type bin will map to GLOBAL_BIN
+ # id is the identifier used as the hash key by the GLOBAL hash
+ # fileLocation is the full path to the file
+ # distroList is an optional list of distros that this particular
+ # file location, if no distro list is presented the file location
+ # is considered to apply to all distros
+ my ($type,$id,$fileLocation,@distroList) = split /\s*,\s*/, $file;
+ $fileLocation =~ s/^\'(.*)\'$/$1/;
+ if($#distroList == -1) {
+ $map{uc($type)}->{$id}=$fileLocation;
+ }
+ else {
+ foreach my $distro (@distroList) {
+ # if the current distro matches the distro listed then
+ # this file location applies
+ if($currentDistro =~ /$distro/) {
+ $map{uc($type)}->{$id}=$fileLocation;
+ }
+ }
+ }
+ }
+ }
+ unless(defined($map{uc("BFILE")}->{"current_config"})) {
+ &setGlobal("BFILE","current_config",&getGlobal("BFILE","config"));
+ }
+}
+
+###############################################################################
+# setServiceInfo($OSServiceMapFile, $currentDistro
+#
+# Given the location of an OS Service map file, which describes
+# a service in terms of configurables, processes and a service type.
+# The subroutine fills out the GLOBAL_SERVICE, $GLOBAL_RC_CONFIG, GLOBAL_SERVTYPE, and
+# GLOBAL_PROCESS hashes for a given service ID.
+###############################################################################
+sub setServiceInfo($$) {
+ my ($serviceInfoFile,$currentDistro) = @_;
+ my @serviceInfo = ();
+
+ if(open(SERVICEINFO, "<$serviceInfoFile" )) {
+
+ @serviceInfo = <SERVICEINFO>;
+
+ close(SERVICEINFO);
+
+ }
+ else {
+ print STDERR "$err Unable to find service, service type, and process information\n" .
+ "$spc for '$distro'.\n" .
+ "$spc Contact the Bastille support list for details.\n";
+ exit(1);
+ }
+
+
+ # The following loop, parses the entire (YOUR OS).service file
+ # to provide service information for YOUR OS.
+ # The files format is as follows:
+ # serviceID,servType,('service' 'configuration' 'list'),('process' 'list')[,DISTROS]*
+ # if distros are not present then the service is assumed to be
+ # relevant the the current distro
+
+
+#
+# More specifically, this file's format for rc-based daemons is:
+#
+# script_name,rc,(rc-config-file rc-config-file ...),(rc-variable1 rc-variable2 ...),('program_name1 program_name2 ...')
+#
+# ...where script_name is a file in /etc/init.d/ and
+# ...program_nameN is a program launced by the script.
+#
+# This file's format for inet-based daemons is:
+#
+# identifier, inet, line name/file name, program name
+#
+# label,inet,(port1 port2 ...),(daemon1 daemon2 ...)
+#
+# ...where label is arbitrary, portN is one of the ports
+# ...this one listens on, and daemonN is a program launched
+# ...in response to a connection on a port.
+
+ foreach my $service (@serviceInfo) {
+ # This file accepts simple whole line comments perl style
+ if($service !~ /^\s+\#|^\s+$/) {
+ chomp $service;
+ my ($serviceID,$servType,$strConfigList,$strServiceList,
+ $strProcessList,@distroList) = split /\s*,\s*/, $service;
+
+ sub MakeArrayFromString($){
+ my $entryString = $_[0];
+ my @destArray = ();
+ if ($entryString =~ /\'\S+\'/) { #Make sure we have something to extract before we try
+ @destArray = split /\'\s+\'/, $entryString;
+ $destArray[0] =~ s/^\(\'(.+)$/$1/; # Remove leading quotation mark
+ $destArray[$#destArray] =~ s/^(.*)\'\)$/$1/; #Remove trailing quotation mark
+ }
+ return @destArray;
+ }
+
+ # produce a list of configuration files from the files
+ # format ('configuration' 'files')
+ my @configList = MakeArrayFromString($strConfigList);
+
+ # produce a list of service configurables from the files
+ # format ('service' 'configurable')
+ my @serviceList = MakeArrayFromString($strServiceList);
+
+ # produce a list of process names from the files format
+ # ('my' 'process' 'list')
+ my @processList = MakeArrayFromString($strProcessList);
+
+ # if distros were not specified then accept the service information
+ if($#distroList == -1) {
+ @{$GLOBAL_SERVICE{$serviceID}} = @serviceList;
+ $GLOBAL_SERVTYPE{$serviceID} = $servType;
+ @{$GLOBAL_PROCESS{$serviceID}} = @processList;
+ @{$GLOBAL_RC_CONFIG{$serviceID}} = @configList;
+ }
+ else {
+ # only if the current distro matches one of the listed distros
+ # include the service information.
+ foreach my $distro (@distroList) {
+ if($currentDistro =~ /$distro/) {
+ @{$GLOBAL_SERVICE{$serviceID}} = @serviceList;
+ $GLOBAL_SERVTYPE{$serviceID} = $servType;
+ @{$GLOBAL_PROCESS{$serviceID}} = @processList;
+ @{$GLOBAL_RC_CONFIG{$serviceID}} = @configList;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+###############################################################################
+# getFileAndServiceInfo($distro,$actualDistro)
+#
+# This subrountine, given distribution information, will import system file
+# and service information into the GLOBA_* hashes.
+#
+# NOTE: $distro and $actualDistro will only differ when the --os switch is
+# used to generate a configuration file for an arbitrary operating
+# system.
+#
+###############################################################################
+sub getFileAndServiceInfo($$) {
+
+ my ($distro,$actualDistro) = @_;
+
+ # defines the path to the OS map information for any supported OS type.
+ # OS map information is used to determine file locations for a given
+ # distribution.
+ my %oSInfoPath = (
+ "LINUX" => "/usr/share/Bastille/OSMap/",
+ "HP-UX" => "/etc/opt/sec_mgmt/bastille/OSMap/",
+ "OSX" => "/usr/share/Bastille/OSMap/"
+ );
+
+ # returns the OS, LINUX, HP-UX, or OSX, associated with this
+ # distribution
+ my $actualOS = &getOSType($actualDistro);
+ my $oS = &getOSType($distro);
+
+ if(defined $actualOS && defined $oS) {
+ my $bastilleInfoFile = $oSInfoPath{$actualOS} . "${actualOS}.bastille";
+ my $systemInfoFile = $oSInfoPath{$actualOS} . "${oS}.system";
+ my $serviceInfoFile = $oSInfoPath{$actualOS} . "${oS}.service";
+
+ if(-f $bastilleInfoFile) {
+ &setFileLocations($bastilleInfoFile,$actualDistro);
+ }
+ else {
+ print STDERR "$err Unable to find bastille file information.\n" .
+ "$spc $bastilleInfoFile does not exist on the system";
+ exit(1);
+ }
+
+ if(-f $systemInfoFile) {
+ &setFileLocations($systemInfoFile,$distro);
+ }
+ else {
+ print STDERR "$err Unable to find system file information.\n" .
+ "$spc $systemInfoFile does not exist on the system";
+ exit(1);
+ }
+ # Service info File is optional
+ if(-f $serviceInfoFile) {
+ &setServiceInfo($serviceInfoFile,$distro);
+ }
+ }
+ else {
+ print STDERR "$err Unable to determine operating system type\n" .
+ "$spc for $actualDistro or $distro\n";
+ exit(1);
+ }
+
+}
+
+
+# returns the Operating System type associated with the specified
+# distribution.
+sub getOSType($) {
+
+ my $distro = $_[0];
+
+ my %supportedOSHash = &getSupportedOSHash;
+ foreach my $oSType (keys %supportedOSHash) {
+ foreach my $oSDistro (@{$supportedOSHash{$oSType}}) {
+ if($distro eq $oSDistro) {
+ return $oSType;
+ }
+ }
+ }
+
+ return undef;
+
+}
+
+
+# Test subroutine used to debug file location info for new Distributions as
+# they are ported.
+sub dumpFileInfo {
+ print "Dumping File Information\n";
+ foreach my $hashref (\%GLOBAL_BIN,\%GLOBAL_DIR,\%GLOBAL_FILE,\%GLOBAL_BFILE,\%GLOBAL_BDIR) {
+ foreach my $id (keys %{$hashref}) {
+ print "$id: ${$hashref}{$id}\n";
+ }
+ print "-----------------------\n\n";
+ }
+}
+
+# Test subroutine used to debug service info for new Distributions as
+# they are ported.
+sub dumpServiceInfo {
+ print "Dumping Service Information\n";
+ foreach my $serviceId (keys %GLOBAL_SERVICE) {
+ print "$serviceId:\n";
+ print "Type - $GLOBAL_SERVTYPE{$serviceId}\n";
+ print "Service List:\n";
+ foreach my $service (@{$GLOBAL_SERVICE{$serviceId}}) {
+ print "$service ";
+ }
+ print "\nProcess List:\n";
+ foreach my $process (@{$GLOBAL_PROCESS{$serviceId}}) {
+ print "$process ";
+ }
+ print "\n----------------------\n";
+ }
+}
+
+
+###########################################################################
+#
+# &ConfigureForDistro configures the API for a given distribution. This
+# includes setting global variables that tell the Bastille API about
+# given binaries and directories.
+#
+# WARNING: If a distro is not covered here, Bastille may not be 100%
+# compatible with it, though 1.1 is written to be much smarter
+# about unknown distros...
+#
+###########################################################################
+sub ConfigureForDistro {
+
+ my $retval=1;
+
+ # checking to see if the os version given is in fact supported
+ my $distro = &GetDistro;
+
+ # checking to see if the actual os version is in fact supported
+ my $actualDistro = &getActualDistro;
+ $ENV{'LOCALE'}=''; # So that test cases checking for english results work ok.
+ if ((! &is_OS_supported($distro)) or (! &is_OS_supported($actualDistro)) ) {
+ # if either is not supported then print out a list of supported versions
+ if (! &is_OS_supported($distro)) {
+ print STDERR "$err '$distro' is not a supported operating system.\n";
+ }
+ else {
+ print STDERR "$err Bastille is unable to operate correctly on this\n";
+ print STDERR "$spc $distro operating system.\n";
+ }
+ my %supportedOSHash = &getSupportedOSHash;
+ print STDERR "$spc Valid operating system versions are as follows:\n";
+
+ foreach my $oSType (keys %supportedOSHash) {
+
+ print STDERR "$spc $oSType:\n$spc ";
+
+ my $os_number = 1;
+ foreach my $os (@{$supportedOSHash{$oSType}}) {
+ print STDERR "'$os' ";
+ if ($os_number == 5){
+ print STDERR "\n$spc ";
+ $os_number = 1;
+ }
+ else {
+ $os_number++;
+ }
+
+ }
+ print STDERR "\n";
+ }
+
+ print "\n" . $GLOBAL_ERROR{"usage"};
+ exit(1);
+ }
+
+ # First, let's make sure that we do not create any files or
+ # directories with more permissive permissions than we
+ # intend via setting the Perl umask
+ umask(077);
+
+ &getFileAndServiceInfo($distro,$actualDistro);
+
+# &dumpFileInfo; # great for debuging file location issues
+# &dumpServiceInfo; # great for debuging service information issues
+
+ # OS dependent error messages (after configuring file locations)
+ my $nodisclaim_file = &getGlobal('BFILE', "nodisclaimer");
+
+ $GLOBAL_ERROR{"disclaimer"}="$err Unable to touch $nodisclaim_file:" .
+ "$spc You must use Bastille\'s -n flag (for example:\n" .
+ "$spc bastille -f -n) or \'touch $nodisclaim_file \'\n";
+
+ return $retval;
+}
+
+
+###########################################################################
+###########################################################################
+# #
+# The B_<perl_function> file utilities are replacements for their Perl #
+# counterparts. These replacements log their actions and their errors, #
+# but are very similar to said counterparts. #
+# #
+###########################################################################
+###########################################################################
+
+
+###########################################################################
+# B_open is used for opening a file for reading. B_open_plus is the preferred
+# function for writing, since it saves a backup copy of the file for
+# later restoration.
+#
+# B_open opens the given file handle, associated with the given filename
+# and logs appropriately.
+#
+###########################################################################
+
+sub B_open {
+ my $retval=1;
+ my ($handle,$filename)=@_;
+
+ unless ($GLOBAL_LOGONLY) {
+ $retval = open $handle,$filename;
+ }
+
+ ($handle) = "$_[0]" =~ /[^:]+::[^:]+::([^:]+)/;
+ &B_log("ACTION","open $handle,\"$filename\";\n");
+ unless ($retval) {
+ &B_log("ERROR","open $handle, $filename failed...\n");
+ }
+
+ return $retval;
+}
+
+###########################################################################
+# B_open_plus is the v1.1 open command.
+#
+# &B_open_plus($handle_file,$handle_original,$file) opens the file $file
+# for reading and opens the file ${file}.bastille for writing. It is the
+# counterpart to B_close_plus, which will move the original file to
+# $GLOBAL_BDIR{"backup"} and will place the new file ${file}.bastille in its
+# place.
+#
+# &B_open_plus makes the appropriate log entries in the action and error
+# logs.
+###########################################################################
+
+sub B_open_plus {
+
+ my ($handle_file,$handle_original,$file)=@_;
+ my $retval=1;
+ my $return_file=1;
+ my $return_old=1;
+
+ my $original_file = $file;
+
+ # Open the original file and open a copy for writing.
+ unless ($GLOBAL_LOGONLY) {
+ # if the temporary filename already exists then the open operation will fail.
+ if ( $file eq "" ){
+ &B_log("ERROR","Internal Error - Attempt Made to Open Blank Filename");
+ $return_old=0;
+ $return_file=0;
+ return 0; #False
+ } elsif (-e "${file}.bastille") {
+ &B_log("ERROR","Unable to open $file as the swap file ".
+ "${file}.bastille\" already exists. Rename the swap ".
+ "file to allow Bastille to make desired file modifications.");
+ $return_old=0;
+ $return_file=0;
+ }
+ else {
+ $return_old = open $handle_original,"$file";
+ $return_file = open $handle_file,("> $file.bastille");
+ }
+ }
+
+ # Error handling/logging here...
+ #&B_log("ACTION","# Modifying file $original_file via temporary file $original_file.bastille\n");
+ unless ($return_file) {
+ $retval=0;
+ &B_log("ERROR","open file: \"$original_file.bastille\" failed...\n");
+ }
+ unless ($return_old) {
+ $retval=0;
+ &B_log("ERROR","open file: \"$original_file\" failed.\n");
+ }
+
+ return $retval;
+
+}
+
+###########################################################################
+# B_close was the v1.0 close command. It is still used in places in the
+# code.
+# However the use of B _close_plus, which implements a new, smarter,
+# backup scheme is preferred.
+#
+# B_close closes the given file handle, associated with the given filename
+# and logs appropriately.
+###########################################################################
+
+
+sub B_close {
+ my $retval=1;
+
+ unless ($GLOBAL_LOGONLY) {
+ $retval = close $_[0];
+ }
+
+ &B_log("ACTION", "close $_[0];\n");
+ unless ($retval) {
+ &B_log("ERROR", "close $_[0] failed...\n");
+ }
+
+ return $retval;
+}
+
+
+###########################################################################
+# B_close_plus is the v1.1 close command.
+#
+# &B_close_plus($handle_file,$handle_original,$file) closes the files
+# $file and ${file}.bastille, backs up $file to $GLOBAL_BDIR{"backup"} and
+# renames ${file}.bastille to $file. This backup is made using the
+# internal API function &B_backup_file. Further, it sets the new file's
+# permissions and uid/gid to the same as the old file.
+#
+# B_close_plus is the counterpart to B_open_plus, which opened $file and
+# $file.bastille with the file handles $handle_original and $handle_file,
+# respectively.
+#
+# &B_close_plus makes the appropriate log entries in the action and error
+# logs.
+###########################################################################
+
+sub B_close_plus {
+ my ($handle_file,$handle_original,$file)=@_;
+ my ($mode,$uid,$gid);
+ my @junk;
+
+ my $original_file;
+
+ my $retval=1;
+ my $return_file=1;
+ my $return_old=1;
+
+ # Append the global prefix, but save the original for B_backup_file b/c
+ # it appends the prefix on its own...
+
+ $original_file=$file;
+
+ #
+ # Close the files and prepare for the rename
+ #
+
+ if (($file eq "") or (not(-e $file ))) {
+ &B_log("ERROR","Internal Error, attempted to close a blank filename ".
+ "or nonexistent file.");
+ return 0; #False
+ }
+
+ unless ($GLOBAL_LOGONLY) {
+ $return_file = close $handle_file;
+ $return_old = close $handle_original;
+ }
+
+ # Error handling/logging here...
+ #&B_log("ACTION","#Closing $original_file and backing up to " . &getGlobal('BDIR', "backup"));
+ #&B_log("ACTION","/$original_file\n");
+
+ unless ($return_file) {
+ $retval=0;
+ &B_log("ERROR","close $original_file failed...\n");
+ }
+ unless ($return_old) {
+ $retval=0;
+ &B_log("ERROR","close $original_file.bastille failed.\n");
+ }
+
+ #
+ # If we've had no errors, backup the old file and put the new one
+ # in its place, with the Right permissions.
+ #
+
+ unless ( ($retval == 0) or $GLOBAL_LOGONLY) {
+
+ # Read the permissions/owners on the old file
+
+ @junk=stat ($file);
+ $mode=$junk[2];
+ $uid=$junk[4];
+ $gid=$junk[5];
+
+ # Set the permissions/owners on the new file
+
+ chmod $mode, "$file.bastille" or &B_log("ERROR","Not able to retain permissions on $original_file!!!\n");
+ chown $uid, $gid, "$file.bastille" or &B_log("ERROR","Not able to retain owners on $original_file!!!\n");
+
+ # Backup the old file and put a new one in place.
+
+ &B_backup_file($original_file);
+ rename "$file.bastille", $file or
+ &B_log("ERROR","B_close_plus: not able to move $original_file.bastille to $original_file\n");
+
+ # We add the file to the GLOBAL_SUMS hash if it is not already present
+ &B_set_sum($file);
+
+ }
+
+ return $retval;
+}
+
+###########################################################################
+# &B_backup_file ($file) makes a backup copy of the file $file in
+# &getGlobal('BDIR', "backup"). Note that this routine is intended for internal
+# use only -- only Bastille API functions should call B_backup_file.
+#
+###########################################################################
+
+sub B_backup_file {
+
+ my $file=$_[0];
+ my $complain = 1;
+ my $original_file = $file;
+
+ my $backup_dir = &getGlobal('BDIR', "backup");
+ my $backup_file = $backup_dir . $original_file;
+
+ my $retval=1;
+
+ # First, separate the file into the directory and the relative filename
+
+ my $directory ="";
+ if ($file =~ /^(.*)\/([^\/]+)$/) {
+ #$relative_file=$2;
+ $directory = $1;
+ } else {
+ $directory=cwd;
+ }
+
+ # Now, if the directory does not exist, create it.
+ # Later:
+ # Try to set the same permissions on the patch directory that the
+ # original had...?
+
+ unless ( -d ($backup_dir . $directory) ) {
+ mkpath(( $backup_dir . $directory),0,0700);
+
+ }
+
+ # Now we backup the file. If there is already a backup file there,
+ # we will leave it alone, since it exists from a previous run and
+ # should be the _original_ (possibly user-modified) distro's version
+ # of the file.
+
+ if ( -e $file ) {
+
+ unless ( -e $backup_file ) {
+ my $command=&getGlobal("BIN","cp");
+ &B_Backtick("$command -p $file $backup_file");
+ &B_revert_log (&getGlobal("BIN","mv"). " $backup_file $file");
+ }
+
+ } else {
+ # The file we were trying to backup doesn't exist.
+
+ $retval=0;
+ # This is a non-fatal error, not worth complaining about
+ $complain = 0;
+ #&ErrorLog ("# Failed trying to backup file $file -- it doesn't exist!\n");
+ }
+
+ # Check to make sure that the file does exist in the backup location.
+
+ unless ( -e $backup_file ) {
+ $retval=0;
+ if ( $complain == 1 ) {
+ &B_log("ERROR","Failed trying to backup $file -- the copy was not created.\n");
+ }
+ }
+
+ return $retval;
+}
+
+
+###########################################################################
+# &B_read_sums reads in the sum.csv file which contains information
+# about Bastille modified files. The file structure is as follows:
+#
+# filename,filesize,cksum
+#
+# It reads the information into the GLOBAL_SUM hash i.e.
+# $GLOBAL_SUM{$file}{sum} = $cksum
+# $GLOBAL_SUM{$file}{filesize} = $size
+# For the first run of Bastille on a given system this subroutine
+# is a no-op, and returns "undefined."
+###########################################################################
+
+sub B_read_sums {
+
+ my $sumFile = &getGlobal('BFILE',"sum.csv");
+
+ if ( -e $sumFile ) {
+
+ open( SUM, "< $sumFile") or &B_log("ERROR","Unable to open $sumFile for read.\n$!\n");
+
+ while( my $line = <SUM> ) {
+ chomp $line;
+ my ($file,$filesize,$sum,$flag) = split /,/, $line;
+ if(-e $file) {
+ $GLOBAL_SUM{"$file"}{filesize} = $filesize;
+ $GLOBAL_SUM{"$file"}{sum} = $sum;
+ }
+ }
+
+ close(SUM);
+ } else {
+ return undef;
+ }
+}
+
+
+###########################################################################
+# &B_write_sums writes out the sum.csv file which contains information
+# about Bastille modified files. The file structure is as follows:
+#
+# filename,filesize,cksum
+#
+# It writes the information from the GLOBAL_SUM hash i.e.
+#
+# $file,$GLOBAL_SUM{$file}{sum},$GLOBAL_SUM{$file}{filesize}
+#
+# This subroutine requires access to the GLOBAL_SUM hash.
+###########################################################################
+
+sub B_write_sums {
+
+ my $sumFile = &getGlobal('BFILE',"sum.csv");
+
+ if ( %GLOBAL_SUM ) {
+
+ open( SUM, "> $sumFile") or &B_log("ERROR","Unable to open $sumFile for write.\n$!\n");
+
+ for my $file (sort keys %GLOBAL_SUM) {
+ if( -e $file) {
+ print SUM "$file,$GLOBAL_SUM{\"$file\"}{filesize},$GLOBAL_SUM{\"$file\"}{sum}\n";
+ }
+ }
+
+ close(SUM);
+ }
+
+}
+
+
+###########################################################################
+# &B_check_sum($file) compares the stored cksum and filesize of the given
+# file compared to the current cksum and filesize respectively.
+# This subroutine also keeps the state of the sum check by setting the
+# checked flag which tells the subroutine that on this run this file
+# has already been checked.
+#
+# $GLOBAL_SUM{$file}{checked} = 1;
+#
+# This subroutine requires access to the GLOBAL_SUM hash.
+#
+# Returns 1 if sum checks out and 0 if not
+###########################################################################
+
+sub B_check_sum($) {
+ my $file = $_[0];
+ my $cksum = &getGlobal('BIN',"cksum");
+
+ if (not(%GLOBAL_SUM)) {
+ &B_read_sums;
+ }
+
+ if(-e $file) {
+ my ($sum,$size,$ckfile) = split(/\s+/, `$cksum $file`);
+ my $commandRetVal = ($? >> 8); # find the command's return value
+
+ if($commandRetVal != 0) {
+ &B_log("ERROR","$cksum reported the following error:\n$!\n");
+ return 0;
+ } else {
+ if ( exists $GLOBAL_SUM{$file} ) {
+ # if the file size or file sum differ from those recorded.
+ if (( $GLOBAL_SUM{$file}{filesize} == $size) and
+ ($GLOBAL_SUM{$file}{sum} == $sum )) {
+ return 1; #True, since saved state matches up, all is well.
+ } else {
+ return 0; #False, since saved state doesn't match
+ }
+ } else {
+ &B_log("ERROR","File: $file does not exist in sums database.");
+ return 0;
+ }
+ }
+ } else {
+ &B_log("ERROR","The file: $file does not exist for comparison in B_check_sum.");
+ return 0;
+ }
+}
+
+# Don't think we need this anymore as function now check_sums returns
+# results directly
+#sub isSumDifferent($) {
+# my $file = $_[0];
+# if(exists $GLOBAL_SUM{$file}) {
+# return $GLOBAL_SUM{$file}{flag}
+# }
+#}
+
+sub listModifiedFiles {
+ my @listModifiedFiles=sort keys %GLOBAL_SUM;
+ return @listModifiedFiles;
+}
+
+###########################################################################
+# &B_isFileinSumDB($file) checks to see if a given file's sum was saved.
+#
+# $GLOBAL_SUM{$file}{filesize} = $size;
+# $GLOBAL_SUM{$file}{sum} = $cksum;
+#
+# This subroutine requires access to the GLOBAL_SUM hash.
+###########################################################################
+
+sub B_isFileinSumDB($) {
+ my $file = $_[0];
+
+ if (not(%GLOBAL_SUM)) {
+ &B_log("DEBUG","Reading in DB from B_isFileinSumDB");
+ &B_read_sums;
+ }
+ if (exists($GLOBAL_SUM{"$file"})){
+ &B_log("DEBUG","$file is in sum database");
+ return 1; #true
+ } else {
+ &B_log("DEBUG","$file is not in sum database");
+ return 0; #false
+ }
+}
+
+###########################################################################
+# &B_set_sum($file) sets the current cksum and filesize of the given
+# file into the GLOBAL_SUM hash.
+#
+# $GLOBAL_SUM{$file}{filesize} = $size;
+# $GLOBAL_SUM{$file}{sum} = $cksum;
+#
+# This subroutine requires access to the GLOBAL_SUM hash.
+###########################################################################
+
+sub B_set_sum($) {
+
+ my $file = $_[0];
+ my $cksum = &getGlobal('BIN',"cksum");
+ if( -e $file) {
+
+ my ($sum,$size,$ckfile) = split(/\s+/, `$cksum $file`);
+ my $commandRetVal = ($? >> 8); # find the command's return value
+
+ if($commandRetVal != 0) {
+
+ &B_log("ERROR","$cksum reported the following error:\n$!\n");
+
+ }
+ else {
+
+ # new file size and sum are added to the hash
+ $GLOBAL_SUM{$file}{filesize} = $size;
+ $GLOBAL_SUM{$file}{sum} = $sum;
+ &B_write_sums;
+
+ }
+ } else {
+ &B_log("ERROR","Can not save chksum for file: $file since it does not exist");
+ }
+}
+
+
+###########################################################################
+#
+# &B_delete_file ($file) deletes the file $file and makes a backup to
+# the backup directory.
+#
+##########################################################################
+
+
+sub B_delete_file($) { #Currently Linux only (TMPDIR)
+ #consideration: should create clear_sum routine if this is ever used to remove
+ # A Bastille-generated file.
+
+ #
+ # This API routine deletes the named file, backing it up first to the
+ # backup directory.
+ #
+
+ my $filename=shift @_;
+ my $retval=1;
+
+ # We have to append the prefix ourselves since we don't use B_open_plus
+
+ my $original_filename=$filename;
+
+ &B_log("ACTION","Deleting (and backing-up) file $original_filename\n");
+ &B_log("ACTION","rm $original_filename\n");
+
+ unless ($filename) {
+ &B_log("ERROR","B_delete_file called with no arguments!\n");
+ }
+
+ unless ($GLOBAL_LOGONLY) {
+ if ( B_backup_file($original_filename) ) {
+ unless ( unlink $filename ) {
+ &B_log("ERROR","Couldn't unlink file $original_filename");
+ $retval=0;
+ }
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","B_delete_file did not delete $original_filename since it could not back it up\n");
+ }
+ }
+
+ $retval;
+
+}
+
+
+###########################################################################
+# &B_create_file ($file) creates the file $file, if it doesn't already
+# exist.
+# It will set a default mode of 0700 and a default uid/gid or 0/0.
+#
+# &B_create_file, to support Bastille's revert functionality, writes an
+# rm $file command to the end of the file &getGlobal('BFILE', "created-files").
+#
+##########################################################################
+
+
+sub B_create_file($) {
+
+ my $file = $_[0];
+ my $retval=1;
+
+ # We have to create the file ourselves since we don't use B_open_plus
+
+ my $original_file = $file;
+
+ if ($file eq ""){
+ &B_log("ERROR","Internal Error, attempt made to create blank filename");
+ return 0; #False
+ }
+
+ unless ( -e $file ) {
+
+ unless ($GLOBAL_LOGONLY) {
+
+ # find the directory in which the file is to reside.
+ my $dirName = dirname($file);
+ # if the directory does not exist then
+ if(! -d $dirName) {
+ # create it.
+ mkpath ($dirName,0,0700);
+ }
+
+ $retval=open CREATE_FILE,">$file";
+
+ if ($retval) {
+ close CREATE_FILE;
+ chmod 0700,$file;
+ # Make the revert functionality
+ &B_revert_log( &getGlobal('BIN','rm') . " $original_file \n");
+ } else {
+ &B_log("ERROR","Couldn't create file $original_file even though " .
+ "it didn't already exist!\n");
+ }
+ }
+ &B_log("ACTION","Created file $original_file\n");
+ } else {
+ &B_log("DEBUG","Didn't create file $original_file since it already existed.\n");
+ $retval=0;
+ }
+
+ $retval;
+}
+
+
+###########################################################################
+# &B_create_dir ($dir) creates the directory $dir, if it doesn't already
+# exist.
+# It will set a default mode of 0700 and a default uid/gid or 0/0.
+#
+##########################################################################
+
+
+sub B_create_dir($) {
+
+ my $dir = $_[0];
+ my $retval=1;
+
+ # We have to append the prefix ourselves since we don't use B_open_plus
+
+ my $original_dir=$dir;
+
+ unless ( -d $dir ) {
+ unless ($GLOBAL_LOGONLY) {
+ $retval=mkdir $dir,0700;
+
+ if ($retval) {
+ # Make the revert functionality
+ &B_revert_log (&getGlobal('BIN','rmdir') . " $original_dir\n");
+ }
+ else {
+ &B_log("ERROR","Couldn't create dir $original_dir even though it didn't already exist!");
+ }
+
+ }
+ &B_log("ACTION","Created directory $original_dir\n");
+ }
+ else {
+ &B_log("ACTION","Didn't create directory $original_dir since it already existed.\n");
+ $retval=0;
+ }
+
+ $retval;
+}
+
+
+
+###########################################################################
+# &B_symlink ($original_file,$new_symlink) creates a symbolic link from
+# $original_file to $new_symlink.
+#
+# &B_symlink respects $GLOBAL_LOGONLY. It supports
+# the revert functionality that you've come to know and love by adding every
+# symbolic link it creates to &getGlobal('BFILE', "created-symlinks"), currently set to:
+#
+# /root/Bastille/revert/revert-created-symlinks
+#
+# The revert script, if it works like I think it should, will run this file,
+# which should be a script or rm's...
+#
+##########################################################################
+
+sub B_symlink($$) {
+ my ($source_file,$new_symlink)=@_;
+ my $retval=1;
+ my $original_source = $source_file;
+ my $original_symlink = $new_symlink;
+
+ unless ($GLOBAL_LOGONLY) {
+ $retval=symlink $source_file,$new_symlink;
+ if ($retval) {
+ &B_revert_log (&getGlobal('BIN',"rm") . " $original_symlink\n");
+ }
+ }
+
+ &B_log("ACTION", "Created a symbolic link called $original_symlink from $original_source\n");
+ &B_log("ACTION", "symlink \"$original_source\",\"$original_symlink\";\n");
+ unless ($retval) {
+ &B_log("ERROR","Couldn't symlink $original_symlink -> $original_source\n");
+ }
+
+ $retval;
+
+}
+
+
+sub B_cp($$) {
+
+ my ($source,$target)=@_;
+ my $retval=0;
+
+ my $had_to_backup_target=0;
+
+ use File::Copy;
+
+ my $original_source=$source;
+ my $original_target=$target;
+
+ if( -e $target and -f $target ) {
+ &B_backup_file($original_target);
+ &B_log("ACTION","About to copy $original_source to $original_target -- had to backup target\n");
+ $had_to_backup_target=1;
+ }
+
+ $retval=copy($source,$target);
+ if ($retval) {
+ &B_log("ACTION","cp $original_source $original_target\n");
+
+ #
+ # We want to add a line to the &getGlobal('BFILE', "created-files") so that the
+ # file we just put at $original_target gets deleted.
+ #
+ &B_revert_log(&getGlobal('BIN',"rm") . " $original_target\n");
+ } else {
+ &B_log("ERROR","Failed to copy $original_source to $original_target\n");
+ }
+ # We add the file to the GLOBAL_SUMS hash if it is not already present
+ &B_set_sum($target);
+ $retval;
+}
+
+
+
+############################################################################
+# &B_place puts a file in place, using Perl's File::cp. This file is taken
+# from &getGlobal('BDIR', "share") and is used to place a file that came with
+# Bastille.
+#
+# This should be DEPRECATED in favor of &B_cp, since the only reason it exists
+# is because of GLOBAL_PREFIX, which has been broken for quite some time.
+# Otherwise, the two routines are identical.
+#
+# It respects $GLOBAL_LOGONLY.
+# If $target is an already-existing file, it is backed up.
+#
+# revert either appends another "rm $target" to &getGlobal('BFILE', "revert-actions") or
+# backs up the file that _was_ there into the &getGlobal('BDIR', "backup"),
+# appending a "mv" to revert-actions to put it back.
+#
+############################################################################
+
+sub B_place { # Only Linux references left (Firewall / TMPDIR)
+
+ my ($source,$target)=@_;
+ my $retval=0;
+
+ my $had_to_backup_target=0;
+
+ use File::Copy;
+
+ my $original_source=$source;
+ $source = &getGlobal('BDIR', "share") . $source;
+ my $original_target=$target;
+
+ if ( -e $target and -f $target ) {
+ &B_backup_file($original_target);
+ &B_log("ACTION","About to copy $original_source to $original_target -- had to backup target\n");
+ $had_to_backup_target=1;
+ }
+ $retval=copy($source,$target);
+ if ($retval) {
+ &B_log("ACTION","placed file $original_source as $original_target\n");
+ #
+ # We want to add a line to the &getGlobal('BFILE', "created-files") so that the
+ # file we just put at $original_target gets deleted.
+ &B_revert_log(&getGlobal('BIN',"rm") . " $original_target\n");
+ } else {
+ &B_log("ERROR","Failed to place $original_source as $original_target\n");
+ }
+
+ # We add the file to the GLOBAL_SUMS hash if it is not already present
+ &B_set_sum($target);
+
+ $retval;
+}
+
+
+
+
+
+#############################################################################
+#############################################################################
+#############################################################################
+
+###########################################################################
+# &B_mknod ($file) creates the node $file, if it doesn't already
+# exist. It uses the prefix and suffix, like this:
+#
+# mknod $prefix $file $suffix
+#
+# This is just a wrapper to the mknod program, which tries to introduce
+# revert functionality, by writing rm $file to the end of the
+# file &getGlobal('BFILE', "created-files").
+#
+##########################################################################
+
+
+sub B_mknod($$$) {
+
+ my ($prefix,$file,$suffix) = @_;
+ my $retval=1;
+
+ # We have to create the filename ourselves since we don't use B_open_plus
+
+ my $original_file = $file;
+
+ unless ( -e $file ) {
+ my $command = &getGlobal("BIN","mknod") . " $prefix $file $suffix";
+
+ if ( system($command) == 0) {
+ # Since system will return 0 on success, invert the error code
+ $retval=1;
+ }
+ else {
+ $retval=0;
+ }
+
+ if ($retval) {
+
+ # Make the revert functionality
+ &B_revert_log(&getGlobal('BIN',"rm") . " $original_file\n");
+ } else {
+ &B_log("ERROR","Couldn't mknod $prefix $original_file $suffix even though it didn't already exist!\n");
+ }
+
+
+ &B_log("ACTION","mknod $prefix $original_file $suffix\n");
+ }
+ else {
+ &B_log("ACTION","Didn't mknod $prefix $original_file $suffix since $original_file already existed.\n");
+ $retval=0;
+ }
+
+ $retval;
+}
+
+###########################################################################
+# &B_revert_log("reverse_command") prepends a command to a shell script. This
+# shell script is intended to be run by bastille -r to reverse the changes that
+# Bastille made, returning the files which Bastille changed to their original
+# state.
+###########################################################################
+
+sub B_revert_log($) {
+
+ my $revert_command = $_[0];
+ my $revert_actions = &getGlobal('BFILE', "revert-actions");
+ my $revertdir= &getGlobal('BDIR', "revert");
+ my @lines;
+
+
+ if (! (-e $revert_actions)) {
+ mkpath($revertdir); #if this doesn't work next line catches
+ if (open REVERT_ACTIONS,">" . $revert_actions){ # create revert file
+ close REVERT_ACTIONS; # chown to root, rwx------
+ chmod 0700,$revert_actions;
+ chown 0,0,$revert_actions;
+ }
+ else {
+ &B_log("FATAL","Can not create revert-actions file: $revert_actions.\n" .
+ " Unable to add the following command to the revert\n" .
+ " actions script: $revert_command\n");
+ }
+
+ }
+
+ &B_open_plus (*REVERT_NEW, *REVERT_OLD, $revert_actions);
+
+ while (my $line=<REVERT_OLD>) { #copy file into @lines
+ push (@lines,$line);
+ }
+ print REVERT_NEW $revert_command . "\n"; #make the revert command first in the new file
+ while (my $line = shift @lines) { #write the rest of the lines of the file
+ print REVERT_NEW $line;
+ }
+ close REVERT_OLD;
+ close REVERT_NEW;
+ if (rename "${revert_actions}.bastille", $revert_actions) { #replace the old file with the new file we
+ chmod 0700,$revert_actions; # just made / mirrors B_close_plus logic
+ chown 0,0,$revert_actions;
+ } else {
+ &B_log("ERROR","B_revert_log: not able to move ${revert_actions}.bastille to ${revert_actions}!!! $!) !!!\n");
+ }
+}
+
+
+###########################################################################
+# &getGlobalConfig($$)
+#
+# returns the requested GLOBAL_CONFIG hash value, ignoring the error
+# if the value does not exist (because every module uses this to find
+# out if the question was answered "Y")
+###########################################################################
+sub getGlobalConfig ($$) {
+ my $module = $_[0];
+ my $key = $_[1];
+ if (exists $GLOBAL_CONFIG{$module}{$key}) {
+ my $answer=$GLOBAL_CONFIG{$module}{$key};
+ &B_log("ACTION","Answer to question $module.$key is \"$answer\".\n");
+ return $answer;
+ } else {
+ &B_log("ACTION","Answer to question $module.$key is undefined.");
+ return undef;
+ }
+}
+
+###########################################################################
+# &getGlobal($$)
+#
+# returns the requested GLOBAL_* hash value, and logs an error
+# if the variable does not exist.
+###########################################################################
+sub getGlobal ($$) {
+ my $type = uc($_[0]);
+ my $key = $_[1];
+
+ # define a mapping from the first argument to the proper hash
+ my %map = ("BIN" => \%GLOBAL_BIN,
+ "FILE" => \%GLOBAL_FILE,
+ "BFILE" => \%GLOBAL_BFILE,
+ "DIR" => \%GLOBAL_DIR,
+ "BDIR" => \%GLOBAL_BDIR,
+ "ERROR" => \%GLOBAL_ERROR,
+ "SERVICE" => \%GLOBAL_SERVICE,
+ "SERVTYPE" => \%GLOBAL_SERVTYPE,
+ "PROCESS" => \%GLOBAL_PROCESS,
+ "RCCONFIG" => \%GLOBAL_RC_CONFIG
+ );
+
+ # check to see if the desired key is in the desired hash
+ if (exists $map{$type}->{$key}) {
+ # get the value from the right hash with the key
+ return $map{$type}->{$key};
+ } else {
+ # i.e. Bastille tried to use $GLOBAL_BIN{'cp'} but it does not exist.
+ # Note that we can't use B_log, since it uses getGlobal ... recursive before
+ # configureForDistro is run.
+ print STDERR "ERROR: Bastille tried to use \$GLOBAL_${type}\{\'$key\'} but it does not exist.\n";
+ return undef;
+ }
+}
+
+###########################################################################
+# &getGlobal($$)
+#
+# sets the requested GLOBAL_* hash value
+###########################################################################
+sub setGlobal ($$$) {
+ my $type = uc($_[0]);
+ my $key = $_[1];
+ my $input_value = $_[2];
+
+ # define a mapping from the first argument to the proper hash
+ my %map = ("BIN" => \%GLOBAL_BIN,
+ "FILE" => \%GLOBAL_FILE,
+ "BFILE" => \%GLOBAL_BFILE,
+ "DIR" => \%GLOBAL_DIR,
+ "BDIR" => \%GLOBAL_BDIR,
+ "ERROR" => \%GLOBAL_ERROR,
+ "SERVICE" => \%GLOBAL_SERVICE,
+ "SERVTYPE" => \%GLOBAL_SERVTYPE,
+ "PROCESS" => \%GLOBAL_PROCESS,
+ );
+
+ if ($map{$type}->{$key} = $input_value) {
+ return 1;
+ } else {
+ &B_log('ERROR','Internal Error, Unable to set global config value:' . $type . ", " .$key);
+ return 0;
+ }
+}
+
+
+###########################################################################
+# &showDisclaimer:
+# Print the disclaimer and wait for 2 minutes for acceptance
+# Do NOT do so if any of the following conditions hold
+# 1. the -n option was used
+# 2. the file ~/.bastille_disclaimer exists
+###########################################################################
+
+sub showDisclaimer($) {
+
+ my $nodisclaim = $_[0];
+ my $nodisclaim_file = &getGlobal('BFILE', "nodisclaimer");
+ my $response;
+ my $WAIT_TIME = 300; # we'll wait for 5 minutes
+ my $developersAnd;
+ my $developersOr;
+ if ($GLOBAL_OS =~ "^HP-UX") {
+ $developersAnd ="HP AND ITS";
+ $developersOr ="HP OR ITS";
+ }else{
+ $developersAnd ="JAY BEALE, THE BASTILLE DEVELOPERS, AND THEIR";
+ $developersOr ="JAY BEALE, THE BASTILLE DEVELOPERS, OR THEIR";
+ }
+ my $DISCLAIMER =
+ "\n" .
+ "Copyright (C) 1999-2006 Jay Beale\n" .
+ "Copyright (C) 1999-2001 Peter Watkins\n" .
+ "Copyright (C) 2000 Paul L. Allen\n" .
+ "Copyright (C) 2001-2007 Hewlett-Packard Development Company, L.P.\n" .
+ "Bastille is free software; you are welcome to redistribute it under\n" .
+ "certain conditions. See the \'COPYING\' file in your distribution for terms.\n\n" .
+ "DISCLAIMER. Use of Bastille can help optimize system security, but does not\n" .
+ "guarantee system security. Information about security obtained through use of\n" .
+ "Bastille is provided on an AS-IS basis only and is subject to change without\n" .
+ "notice. Customer acknowledges they are responsible for their system\'s security.\n" .
+ "TO THE EXTENT ALLOWED BY LOCAL LAW, Bastille (\"SOFTWARE\") IS PROVIDED TO YOU \n" .
+ "\"AS IS\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER ORAL OR WRITTEN,\n" .
+ "EXPRESS OR IMPLIED. $developersAnd SUPPLIERS\n" .
+ "DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE \n" .
+ "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n" .
+ "Some countries, states and provinces do not allow exclusions of implied\n" .
+ "warranties or conditions, so the above exclusion may not apply to you. You may\n" .
+ "have other rights that vary from country to country, state to state, or province\n" .
+ "to province. EXCEPT TO THE EXTENT PROHIBITED BY LOCAL LAW, IN NO EVENT WILL\n" .
+ "$developersOr SUBSIDIARIES, AFFILIATES OR\n" .
+ "SUPPLIERS BE LIABLE FOR DIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR OTHER\n" .
+ "DAMAGES (INCLUDING LOST PROFIT, LOST DATA, OR DOWNTIME COSTS), ARISING OUT OF\n" .
+ "THE USE, INABILITY TO USE, OR THE RESULTS OF USE OF THE SOFTWARE, WHETHER BASED\n" .
+ "IN WARRANTY, CONTRACT, TORT OR OTHER LEGAL THEORY, AND WHETHER OR NOT ADVISED\n" .
+ "OF THE POSSIBILITY OF SUCH DAMAGES. Your use of the Software is entirely at your\n" .
+ "own risk. Should the Software prove defective, you assume the entire cost of all\n" .
+ "service, repair or correction. Some countries, states and provinces do not allow\n" .
+ "the exclusion or limitation of liability for incidental or consequential \n" .
+ "damages, so the above limitation may not apply to you. This notice will only \n".
+ "display on the first run on a given system.\n".
+ "To suppress the disclaimer on other machines, use Bastille\'s -n flag (example: bastille -n).\n";
+
+
+# If the user has specified not to show the disclaimer, or
+# the .bastille_disclaimer file already exists, then return
+ if( ( $nodisclaim ) || -e $nodisclaim_file ) { return 1; }
+
+# otherwise, show the disclaimer
+ print ($DISCLAIMER);
+
+# there is a response
+ my $touch = &getGlobal('BIN', "touch");
+ my $retVal = system("$touch $nodisclaim_file");
+ if( $retVal != 0 ) {
+ &ErrorLog ( &getGlobal('ERROR','disclaimer'));
+ }
+} # showDisclaimer
+
+
+
+
+################################################################
+# &systemCall
+#Function used by exported methods B_Backtick and B_system
+#to handle the mechanics of system calls.
+# This function also manages error handling.
+# Input: a system call
+# Output: a list containing the status, sstdout and stderr
+# of the the system call
+#
+################################################################
+sub systemCall ($){
+ no strict;
+ local $command=$_[0]; # changed scoping so eval below can read it
+
+ local $SIG{'ALRM'} = sub { die "timeout" }; # This subroutine exits the "eval" below. The program
+ # can then move on to the next operation. Used "local"
+ # to avoid name space collision with disclaim alarm.
+ local $WAIT_TIME=120; # Wait X seconds for system commands
+ local $commandOutput = '';
+ my $errOutput = '';
+ eval{
+ $errorFile = &getGlobal('BFILE','stderrfile');
+ unlink($errorFile); #To make sure we don't mix output
+ alarm($WAIT_TIME); # start a time-out for command to complete. Some commands hang, and we want to
+ # fail gracefully. When we call "die" it exits this eval statement
+ # with a value we use below
+ $commandOutput = `$command 2> $errorFile`; # run the command and gather its output
+ my $commandRetVal = ($? >> 8); # find the commands return value
+ if ($commandRetVal == 0) {
+ &B_log("ACTION","Executed Command: " . $command . "\n");
+ &B_log("ACTION","Command Output: " . $commandOutput . "\n");
+ die "success";
+ } else {
+ die "failure";
+ };
+ };
+
+ my $exitcode=$@;
+ alarm(0); # End of the timed operation
+
+ my $cat = &getGlobal("BIN","cat");
+ if ( -e $errorFile ) {
+ $errOutput = `$cat $errorFile`;
+ }
+
+ if ($exitcode) { # The eval command above will exit with one of the 3 values below
+ if ($exitcode =~ /timeout/) {
+ &B_log("WARNING","No response received from $command after $WAIT_TIME seconds.\n" .
+ "Command Output: " . $commandOutput . "\n");
+ return (0,'','');
+ } elsif ($exitcode =~ /success/) {
+ return (1,$commandOutput,$errOutput);
+ } elsif ($exitcode =~ /failure/) {
+ return (0,$commandOutput,$errOutput);
+ } else {
+ &B_log("FATAL","Unexpected return state from command execution: $command\n" .
+ "Command Output: " . $commandOutput . "\n");
+ }
+ }
+}
+
+#############################################
+# Use this **only** for commands used that are
+# intended to test system state and
+# not make any system change. Use this in place of the
+# prior use of "backticks throughout Bastille
+# Handles basic output redirection, but not for stdin
+# Input: Command
+# Output: Results
+#############################################
+
+sub B_Backtick($) {
+ my $command=$_[0];
+ my $combineOutput=0;
+ my $stdoutRedir = "";
+ my $stderrRedir = "";
+ my $echo = &getGlobal('BIN','echo');
+
+ if (($command =~ s/2>&1//) or
+ (s/>&2//)){
+ $combineOutput=1;
+ }
+ if ($command =~ s/>\s*([^>\s])+// ) {
+ $stdoutRedir = $1;
+ }
+ if ($command =~ s/2>\s*([^>\s])+// ) {
+ $stderrRedir = $1;
+ }
+
+ my ($ranFine, $stdout, $stderr) = &systemCall($command);
+ if ($ranFine) {
+ &B_log("DEBUG","Command: $command succeeded for test with output: $stdout , ".
+ "and stderr: $stderr");
+ } else {
+ &B_log("DEBUG","Command: $command failed for test with output: $stdout , ".
+ "and stderr: $stderr");
+ }
+ if ($combineOutput) {
+ $stdout .= $stderr;
+ $stderr = $stdout; #these should be the same
+ }
+ if ($stdoutRedir ne "") {
+ system("$echo \'$stdout\' > $stdoutRedir");
+ }
+ if ($stderrRedir ne "") {
+ system("$echo \'$stderr\' > $stderrRedir");
+ }
+ return $stdout;
+}
+
+####################################################################
+# &B_System($command,$revertcommand);
+# This function executes a command, then places the associated
+# revert command in revert file. It takes two parameters, the
+# command and the command that reverts that command.
+#
+# uses ActionLog and ErrorLog for logging purposes.
+###################################################################
+sub B_System ($$) {
+ my ($command,$revertcmd)=@_;
+
+ my ($ranFine, $stdout, $stderr) = &systemCall($command);
+ if ($ranFine) {
+ &B_revert_log ("$revertcmd \n");
+ if ($stderr ne '' ) {
+ &B_log("ACTION",$command . "suceeded with STDERR: " .
+ $stderr . "\n");
+ }
+ return 1;
+ } else {
+ my $warningString = "Command Failed: " . $command . "\n" .
+ "Command Output: " . $stdout . "\n";
+ if ($stderr ne '') {
+ $warningString .= "Error message: " . $stderr;
+ }
+ &B_log("WARNING", $warningString);
+ return 0;
+ }
+}
+
+
+###########################################################################
+# &isProcessRunning($procPattern);
+#
+# If called in scalar context this subroutine will return a 1 if the
+# pattern specified can be matched against the process table. It will
+# return a 0 otherwise.
+# If called in the list context this subroutine will return the list
+# of processes which matched the pattern supplied
+#
+# scalar return values:
+# 0: pattern not in process table
+# 1: pattern is in process table
+#
+# list return values:
+# proc lines from the process table if they are found
+###########################################################################
+sub isProcessRunning($) {
+
+ my $procPattern= $_[0];
+ my $ps = &getGlobal('BIN',"ps");
+
+ my $isRunning=0;
+ # process table.
+ my @psTable = `$ps -elf`;
+ # list of processes that match the $procPattern
+ my @procList;
+ foreach my $process (@psTable) {
+ if($process =~ $procPattern) {
+ $isRunning = 1;
+ push @procList, $process . "\n";
+ }
+ }
+
+ &B_log("DEBUG","$procPattern search yielded $isRunning\n\n");
+ # if this subroutine was called in scalar context
+ if( ! wantarray ) {
+ return $isRunning;
+ }
+
+ return @procList;
+}
+
+
+###########################################################################
+# &checkProcsForService($service);
+#
+# Checks if the given service is running by analyzing the process table.
+# This is a helper function to checkServiceOnLinux and checkServiceOnHP
+#
+# Return values:
+# SECURE_CANT_CHANGE() if the service is off
+# INCONSISTENT() if the state of the service cannot be determined
+#
+# Mostly used in "check service" direct-return context, but added option use.
+# to ignore warning if a check for a service ... where a found service doesn't
+# have direct security problems.
+#
+###########################################################################
+sub checkProcsForService ($;$) {
+ my $service=$_[0];
+ my $ignore_warning=$_[1];
+
+ my @psnames=@{ &getGlobal('PROCESS',$service)};
+
+ my @processes;
+ # inetd services don't have a separate process
+ foreach my $psname (@psnames) {
+ my @procList = &isProcessRunning($psname);
+ if(@procList >= 0){
+ splice @processes,$#processes+1,0,@procList;
+ }
+ }
+
+ if($#processes >= 0){
+ if ((defined($ignore_warning)) and ($ignore_warning eq "ignore_warning")) {
+ &B_log("WARNING","The following processes were still running even though " .
+ "the corresponding service appears to be turned off. Bastille " .
+ "question and action will be skipped.\n\n" .
+ "@processes\n\n");
+ # processes were still running, service is not off, but we don't know how
+ # to configure it so we skip the question
+ return INCONSISTENT();
+ } else {
+ return NOTSECURE_CAN_CHANGE(); # In the case we're ignoring the warning,
+ # ie: checking to make *sure* a process
+ # is running, the answer isn't inconsistent
+ }
+ } else {
+ &B_log("DEBUG","$service is off. Found no processes running on the system.");
+ # no processes, so service is off
+ return SECURE_CANT_CHANGE();
+ }
+ # Can't determine the state of the service by looking at the processes,
+ # so return INCONSISTENT().
+ return INCONSISTENT();
+}
+
+###########################################################################
+# B_parse_fstab()
+#
+# Search the filesystem table for a specific mount point.
+#
+# scalar return value:
+# The line form the table that matched the mount point, or the null string
+# if no match was found.
+#
+# list return value:
+# A list of parsed values from the line of the table that matched, with
+# element [3] containing a reference to a hash of the mount options. The
+# keys are: acl, dev, exec, rw, suid, sync, or user. The value of each key
+# can be either 0 or 1. To access the hash, use code similar to this:
+# %HashResult = %{(&B_parse_fstab($MountPoint))[3]};
+#
+###########################################################################
+
+sub B_parse_fstab($)
+{
+ my $name = shift;
+ my $file = &getGlobal('FILE','fstab');
+ my ($enable, $disable, $infile);
+ my @lineopt;
+ my $retline = "";
+ my @retlist = ();
+
+ unless (open FH, $file) {
+ &B_log('ERROR',"B_parse_fstab couldn't open fstab file at path $file.\n");
+ return 0;
+ }
+ while (<FH>) {
+ s/\#.*//;
+ next unless /\S/;
+ @retlist = split;
+ next unless $retlist[1] eq $name;
+ $retline .= $_;
+ if (wantarray) {
+ my $option = { # initialize to defaults
+ acl => 0, # for ext2, etx3, reiserfs
+ dev => 1,
+ exec => 1,
+ rw => 1,
+ suid => 1,
+ sync => 0,
+ user => 0,
+ };
+
+ my @lineopt = split(',',$retlist[3]);
+ foreach my $entry (@lineopt) {
+ if ($entry eq 'acl') {
+ $option->{'acl'} = 1;
+ }
+ elsif ($entry eq 'nodev') {
+ $option->{'dev'} = 0;
+ }
+ elsif ($entry eq 'noexec') {
+ $option->{'exec'} = 0;
+ }
+ elsif ($entry eq 'ro') {
+ $option->{'rw'} = 0;
+ }
+ elsif ($entry eq 'nosuid') {
+ $option->{'suid'} = 0;
+ }
+ elsif ($entry eq 'sync') {
+ $option->{'sync'} = 1;
+ }
+ elsif ($entry eq 'user') {
+ $option->{'user'} = 1;
+ }
+ }
+ $retlist[3]= $option;
+ }
+ last;
+ }
+
+ if (wantarray)
+ {
+ return @retlist;
+ }
+ else
+ {
+ return $retline;
+ }
+
+}
+
+
+###########################################################################
+# B_parse_mtab()
+#
+# This routine returns a hash of devices and their mount points from mtab,
+# simply so you can get a list of mounted filesystems.
+#
+###########################################################################
+
+sub B_parse_mtab
+{
+ my $mountpoints;
+ open(MTAB,&getGlobal('FILE','mtab'));
+ while(my $mtab_line = <MTAB>) {
+ #test if it's a device
+ if ($mtab_line =~ /^\//)
+ {
+ #parse out device and mount point
+ $mtab_line =~ /^(\S+)\s+(\S+)/;
+ $mountpoints->{$1} = $2;
+ }
+ }
+ return $mountpoints;
+}
+
+
+###########################################################################
+# B_is_rpm_up_to_date()
+#
+#
+###########################################################################
+
+sub B_is_rpm_up_to_date(@)
+{
+ my($nameB,$verB,$relB,$epochB) = @_;
+ my $installedpkg = $nameB;
+
+ if ($epochB =~ /(none)/) {
+ $epochB = 0;
+ }
+
+ my $rpmA = `rpm -q --qf '%{VERSION}-%{RELEASE}-%{EPOCH}\n' $installedpkg`;
+ my $nameA = $nameB;
+ my ($verA,$relA,$epochA);
+
+ my $retval;
+
+ # First, if the RPM isn't installed, let's handle that.
+ if ($rpmA =~ /is not installed/) {
+ $retval = -1;
+ return $retval;
+ }
+ else {
+ # Next, let's try to parse the EVR information without as few
+ # calls as possible to rpm.
+ if ($rpmA =~ /([^-]+)-([^-]+)-([^-]+)$/) {
+ $verA = $1;
+ $relA = $2;
+ $epochA = $3;
+ }
+ else {
+ $nameA = `rpm -q --qf '%{NAME}' $installedpkg`;
+ $verA = `rpm -q --qf '%{VERSION}' $installedpkg`;
+ $relA = `rpm -q --qf '%{RELEASE}' $installedpkg`;
+ $epochA = `rpm -q --qf '%{EPOCH}' $installedpkg`;
+ }
+ }
+
+ # Parse "none" as 0.
+ if ($epochA =~ /(none)/) {
+ $epochA = 0;
+ }
+
+ # Handle the case where only one of them is zero.
+ if ($epochA == 0 xor $epochB == 0)
+ {
+ if ($epochA != 0)
+ {
+ $retval = 1;
+ }
+ else
+ {
+ $retval = 0;
+ }
+ }
+ else
+ {
+ # ...otherwise they are either both 0 or both non-zero and
+ # so the situation isn't trivial.
+
+ # Check epoch first - highest epoch wins.
+ my $rpmcmp = &cmp_vers_part($epochA, $epochB);
+ #print "epoch rpmcmp is $rpmcmp\n";
+ if ($rpmcmp > 0)
+ {
+ $retval = 1;
+ }
+ elsif ($rpmcmp < 0)
+ {
+ $retval = 0;
+ }
+ else
+ {
+ # Epochs were the same. Check Version now.
+ $rpmcmp = &cmp_vers_part($verA, $verB);
+ #print "epoch rpmcmp is $rpmcmp\n";
+ if ($rpmcmp > 0)
+ {
+ $retval = 1;
+ }
+ elsif ($rpmcmp < 0)
+ {
+ $retval = 0;
+ }
+ else
+ {
+ # Versions were the same. Check Release now.
+ my $rpmcmp = &cmp_vers_part($relA, $relB);
+ #print "epoch rpmcmp is $rpmcmp\n";
+ if ($rpmcmp >= 0)
+ {
+ $retval = 1;
+ }
+ elsif ($rpmcmp < 0)
+ {
+ $retval = 0;
+ }
+ }
+ }
+ }
+ return $retval;
+}
+
+#################################################
+# Helper function for B_is_rpm_up_to_date()
+#################################################
+
+#This cmp_vers_part function taken from Kirk Bauer's Autorpm.
+# This version comparison code was sent in by Robert Mitchell and, although
+# not yet perfect, is better than the original one I had. He took the code
+# from freshrpms and did some mods to it. Further mods by Simon Liddington
+# <sjl96v@ecs.soton.ac.uk>.
+#
+# Splits string into minors on . and change from numeric to non-numeric
+# characters. Minors are compared from the beginning of the string. If the
+# minors are both numeric then they are numerically compared. If both minors
+# are non-numeric and a single character they are alphabetically compared, if
+# they are not a single character they are checked to be the same if the are not
+# the result is unknown (currently we say the first is newer so that we have
+# a choice to upgrade). If one minor is numeric and one non-numeric then the
+# numeric one is newer as it has a longer version string.
+# We also assume that (for example) .15 is equivalent to 0.15
+
+sub cmp_vers_part($$) {
+ my($va, $vb) = @_;
+ my(@va_dots, @vb_dots);
+ my($a, $b);
+ my($i);
+
+ if ($vb !~ /^pre/ and $va =~ s/^pre(\d+.*)$/$1/) {
+ if ($va eq $vb) { return -1; }
+ } elsif ($va !~ /^pre/ and $vb =~ s/^pre(\d+.*)$/$1/) {
+ if ($va eq $vb) { return 1; }
+ }
+
+ @va_dots = split(/\./, $va);
+ @vb_dots = split(/\./, $vb);
+
+ $a = shift(@va_dots);
+ $b = shift(@vb_dots);
+ # We also assume that (for example) .15 is equivalent to 0.15
+ if ($a eq '' && $va ne '') { $a = "0"; }
+ if ($b eq '' && $vb ne '') { $b = "0"; }
+ while ((defined($a) && $a ne '') || (defined($b) && $b ne '')) {
+ # compare each minor from left to right
+ if ((not defined($a)) || ($a eq '')) { return -1; } # the longer version is newer
+ if ((not defined($b)) || ($b eq '')) { return 1; }
+ if ($a =~ /^\d+$/ && $b =~ /^\d+$/) {
+ # I have changed this so that when the two strings are numeric, but one or both
+ # of them start with a 0, then do a string compare - Kirk Bauer - 5/28/99
+ if ($a =~ /^0/ or $b =~ /^0/) {
+ # We better string-compare so that netscape-4.6 is newer than netscape-4.08
+ if ($a ne $b) {return ($a cmp $b);}
+ }
+ # numeric compare
+ if ($a != $b) { return $a <=> $b; }
+ } elsif ($a =~ /^\D+$/ && $b =~ /^\D+$/) {
+ # string compare
+ if (length($a) == 1 && length($b) == 1) {
+ # only minors with one letter seem to be useful for versioning
+ if ($a ne $b) { return $a cmp $b; }
+ } elsif (($a cmp $b) != 0) {
+ # otherwise we should at least check they are the same and if not say unknown
+ # say newer for now so at least we get choice whether to upgrade or not
+ return -1;
+ }
+ } elsif ( ($a =~ /^\D+$/ && $b =~ /^\d+$/) || ($a =~ /^\d+$/ && $b =~ /^\D+$/) ) {
+ # if we get a number in one and a word in another the one with a number
+ # has a longer version string
+ if ($a =~ /^\d+$/) { return 1; }
+ if ($b =~ /^\d+$/) { return -1; }
+ } else {
+ # minor needs splitting
+ $a =~ /\d+/ || $a =~ /\D+/;
+ # split the $a minor into numbers and non-numbers
+ my @va_bits = ($`, $&, $');
+ $b =~ /\d+/ || $b =~ /\D+/;
+ # split the $b minor into numbers and non-numbers
+ my @vb_bits = ($`, $&, $');
+ for ( my $j=2; $j >= 0; $j--) {
+ if ($va_bits[$j] ne '') { unshift(@va_dots,$va_bits[$j]); }
+ if ($vb_bits[$j] ne '') { unshift(@vb_dots,$vb_bits[$j]); }
+ }
+ }
+ $a = shift(@va_dots);
+ $b = shift(@vb_dots);
+ }
+ return 0;
+}
+
+1;
+
diff --git a/recipes-security/bastille/files/AccountPermission.pm b/recipes-security/bastille/files/AccountPermission.pm
new file mode 100644
index 0000000..cfbaab1
--- /dev/null
+++ b/recipes-security/bastille/files/AccountPermission.pm
@@ -0,0 +1,1060 @@
+package Bastille::API::AccountPermission;
+use strict;
+
+use Bastille::API;
+
+use Bastille::API::HPSpecific;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(
+B_chmod
+B_chmod_if_exists
+B_chown
+B_chown_link
+B_chgrp
+B_chgrp_link
+B_userdel
+B_groupdel
+B_remove_user_from_group
+B_check_owner_group
+B_is_unowned_file
+B_is_ungrouped_file
+B_check_permissions
+B_permission_test
+B_find_homes
+B_is_executable
+B_is_suid
+B_is_sgid
+B_get_user_list
+B_get_group_list
+B_remove_suid
+);
+our @EXPORT = @EXPORT_OK;
+
+###########################################################################
+# &B_chmod ($mode, $file) sets the mode of $file to $mode. $mode must
+# be stored in octal, so if you want to give mode 700 to /etc/aliases,
+# you need to use:
+#
+# &B_chmod ( 0700 , "/etc/aliases");
+#
+# where the 0700 denotes "octal 7-0-0".
+#
+# &B_chmod ($mode_changes,$file) also respects the symbolic methods of
+# changing file permissions, which are often what question authors are
+# really seeking.
+#
+# &B_chmod ("u-s" , "/bin/mount")
+# or
+# &B_chmod ("go-rwx", "/bin/mount")
+#
+#
+# &B_chmod respects GLOBAL_LOGONLY and uses
+# &B_revert_log used to insert a shell command that will return
+# the permissions to the pre-Bastille state.
+#
+# B_chmod allow for globbing now, as of 1.2.0. JJB
+#
+##########################################################################
+
+
+sub B_chmod($$) {
+ my ($new_perm,$file_expr)=@_;
+ my $old_perm;
+ my $old_perm_raw;
+ my $new_perm_formatted;
+ my $old_perm_formatted;
+
+ my $retval=1;
+
+ my $symbolic = 0;
+ my ($chmod_noun,$add_remove,$capability) = ();
+ # Handle symbolic possibilities too
+ if ($new_perm =~ /([ugo]+)([+-]{1})([rwxst]+)/) {
+ $symbolic = 1;
+ $chmod_noun = $1;
+ $add_remove = $2;
+ $capability = $3;
+ }
+
+ my $file;
+ my @files = glob ($file_expr);
+
+ foreach $file (@files) {
+
+ # Prepend global prefix, but save the original filename for B_backup_file
+ my $original_file=$file;
+
+ # Store the old permissions so that we can log them.
+ unless (stat $file) {
+ &B_log("ERROR","Couldn't stat $original_file from $old_perm to change permissions\n");
+ next;
+ }
+
+ $old_perm_raw=(stat(_))[2];
+ $old_perm= (($old_perm_raw/512) % 8) .
+ (($old_perm_raw/64) % 8) .
+ (($old_perm_raw/8) % 8) .
+ ($old_perm_raw % 8);
+
+ # If we've gone symbolic, calculate the new permissions in octal.
+ if ($symbolic) {
+ #
+ # We calculate the new permissions by applying a bitmask to
+ # the current permissions, by OR-ing (for +) or XOR-ing (for -).
+ #
+ # We create this mask by first calculating a perm_mask that forms
+ # the right side of this, then multiplying it by 8 raised to the
+ # appropriate power to affect the correct digit of the octal mask.
+ # This means that we raise 8 to the power of 0,1,2, or 3, based on
+ # the noun of "other","group","user", or "suid/sgid/sticky".
+ #
+ # Actually, we handle multiple nouns by summing powers of 8.
+ #
+ # The only tough part is that we have to handle suid/sgid/sticky
+ # differently.
+ #
+
+ # We're going to calculate a mask to OR or XOR with the current
+ # file mode. This mask is $mask. We calculate this by calculating
+ # a sum of powers of 8, corresponding to user/group/other,
+ # multiplied with a $premask. The $premask is simply the
+ # corresponding bitwise expression of the rwx bits.
+ #
+ # To handle SUID, SGID or sticky in the simplest way possible, we
+ # simply add their values to the $mask first.
+
+ my $perm_mask = 00;
+ my $mask = 00;
+
+ # Check for SUID, SGID or sticky as these are exceptional.
+ if ($capability =~ /s/) {
+ if ($chmod_noun =~ /u/) {
+ $mask += 04000;
+ }
+ if ($chmod_noun =~ /g/) {
+ $mask += 02000;
+ }
+ }
+ if ($capability =~ /t/) {
+ $mask += 01000;
+ }
+
+ # Now handle the normal attributes
+ if ($capability =~ /[rwx]/) {
+ if ($capability =~ /r/) {
+ $perm_mask |= 04;
+ }
+ if ($capability =~ /w/) {
+ $perm_mask |= 02;
+ }
+ if ($capability =~ /x/) {
+ $perm_mask |= 01;
+ }
+
+ # Now figure out which 3 bit octal digit we're affecting.
+ my $power = 0;
+ if ($chmod_noun =~ /u/) {
+ $mask += $perm_mask * 64;
+ }
+ if ($chmod_noun =~ /g/) {
+ $mask += $perm_mask * 8;
+ }
+ if ($chmod_noun =~ /o/) {
+ $mask += $perm_mask * 1;
+ }
+ }
+ # Now apply the mask to get the new permissions
+ if ($add_remove eq '+') {
+ $new_perm = $old_perm_raw | $mask;
+ }
+ elsif ($add_remove eq '-') {
+ $new_perm = $old_perm_raw & ( ~($mask) );
+ }
+ }
+
+ # formating for simple long octal output of the permissions in string form
+ $new_perm_formatted=sprintf "%5lo",$new_perm;
+ $old_perm_formatted=sprintf "%5lo",$old_perm_raw;
+
+ &B_log("ACTION","change permissions on $original_file from $old_perm_formatted to $new_perm_formatted\n");
+
+ &B_log("ACTION", "chmod $new_perm_formatted,\"$original_file\";\n");
+
+ # Change the permissions on the file
+
+ if ( -e $file ) {
+ unless ($GLOBAL_LOGONLY) {
+ $retval=chmod $new_perm,$file;
+ if($retval){
+ # if the distribution is HP-UX then the modifications should
+ # also be made to the IPD (installed product database)
+ if(&GetDistro =~ "^HP-UX"){
+ &B_swmodify($file);
+ }
+ # making changes revert-able
+ &B_revert_log(&getGlobal('BIN', "chmod") . " $old_perm $file\n");
+ }
+ }
+ unless ($retval) {
+ &B_log("ERROR","Couldn't change permissions on $original_file from $old_perm_formatted to $new_perm_formatted\n");
+ $retval=0;
+ }
+ }
+ else {
+ &B_log("ERROR", "chmod: File $original_file doesn't exist!\n");
+ $retval=0;
+ }
+ }
+
+ $retval;
+
+}
+
+###########################################################################
+# &B_chmod_if_exists ($mode, $file) sets the mode of $file to $mode *if*
+# $file exists. $mode must be stored in octal, so if you want to give
+# mode 700 to /etc/aliases, you need to use:
+#
+# &B_chmod_if_exists ( 0700 , "/etc/aliases");
+#
+# where the 0700 denotes "octal 7-0-0".
+#
+# &B_chmod_if_exists respects GLOBAL_LOGONLY and uses
+# &B_revert_log to reset the permissions of the file.
+#
+# B_chmod_if_exists allow for globbing now, as of 1.2.0. JJB
+#
+##########################################################################
+
+
+sub B_chmod_if_exists($$) {
+ my ($new_perm,$file_expr)=@_;
+ # If $file_expr has a glob character, pass it on (B_chmod won't complain
+ # about nonexistent files if given a glob pattern)
+ if ( $file_expr =~ /[\*\[\{]/ ) { # } just to match open brace for vi
+ &B_log("ACTION","Running chmod $new_perm $file_expr");
+ return(&B_chmod($new_perm,$file_expr));
+ }
+ # otherwise, test for file existence
+ if ( -e $file_expr ) {
+ &B_log("ACTION","File exists, running chmod $new_perm $file_expr");
+ return(&B_chmod($new_perm,$file_expr));
+ }
+}
+
+###########################################################################
+# &B_chown ($uid, $file) sets the owner of $file to $uid, like this:
+#
+# &B_chown ( 0 , "/etc/aliases");
+#
+# &B_chown respects $GLOBAL_LOGONLY and uses
+# &B_revert_log to insert a shell command that will return
+# the file/directory owner to the pre-Bastille state.
+#
+# Unlike Perl, we've broken the chown function into B_chown/B_chgrp to
+# make error checking simpler.
+#
+# As of 1.2.0, this now supports file globbing. JJB
+#
+##########################################################################
+
+
+sub B_chown($$) {
+ my ($newown,$file_expr)=@_;
+ my $oldown;
+ my $oldgown;
+
+ my $retval=1;
+
+ my $file;
+ my @files = glob($file_expr);
+
+ foreach $file (@files) {
+
+ # Prepend prefix, but save original filename
+ my $original_file=$file;
+
+ $oldown=(stat $file)[4];
+ $oldgown=(stat $file)[5];
+
+ &B_log("ACTION","change ownership on $original_file from $oldown to $newown\n");
+ &B_log("ACTION","chown $newown,$oldgown,\"$original_file\";\n");
+ if ( -e $file ) {
+ unless ($GLOBAL_LOGONLY) {
+ # changing the files owner using perl chown function
+ $retval = chown $newown,$oldgown,$file;
+ if($retval){
+ # if the distribution is HP-UX then the modifications should
+ # also be made to the IPD (installed product database)
+ if(&GetDistro =~ "^HP-UX"){
+ &B_swmodify($file);
+ }
+ # making ownership change revert-able
+ &B_revert_log(&getGlobal('BIN', "chown") . " $oldown $file\n");
+ }
+ }
+ unless ($retval) {
+ &B_log("ERROR","Couldn't change ownership to $newown on file $original_file\n");
+ }
+ }
+ else {
+ &B_log("ERROR","chown: File $original_file doesn't exist!\n");
+ $retval=0;
+ }
+ }
+
+ $retval;
+}
+
+###########################################################################
+# &B_chown_link just like &B_chown but one exception:
+# if the input file is a link it will not change the target's ownship, it only change the link itself's ownship
+###########################################################################
+sub B_chown_link($$){
+ my ($newown,$file_expr)=@_;
+ my $chown = &getGlobal("BIN","chown");
+ my @files = glob($file_expr);
+ my $retval = 1;
+
+ foreach my $file (@files) {
+ # Prepend prefix, but save original filename
+ my $original_file=$file;
+ my $oldown=(stat $file)[4];
+ my $oldgown=(stat $file)[5];
+
+ &B_log("ACTION","change ownership on $original_file from $oldown to $newown\n");
+ &B_log("ACTION","chown -h $newown,\"$original_file\";\n");
+ if ( -e $file ) {
+ unless ($GLOBAL_LOGONLY) {
+ `$chown -h $newown $file`;
+ $retval = ($? >> 8);
+ if($retval == 0 ){
+ # if the distribution is HP-UX then the modifications should
+ # also be made to the IPD (installed product database)
+ if(&GetDistro =~ "^HP-UX"){
+ &B_swmodify($file);
+ }
+ # making ownership change revert-able
+ &B_revert_log("$chown -h $oldown $file\n");
+ }
+ }
+ unless ( ! $retval) {
+ &B_log("ERROR","Couldn't change ownership to $newown on file $original_file\n");
+ }
+ }
+ else {
+ &B_log("ERROR","chown: File $original_file doesn't exist!\n");
+ $retval=0;
+ }
+ }
+}
+
+
+###########################################################################
+# &B_chgrp ($gid, $file) sets the group owner of $file to $gid, like this:
+#
+# &B_chgrp ( 0 , "/etc/aliases");
+#
+# &B_chgrp respects $GLOBAL_LOGONLY and uses
+# &B_revert_log to insert a shell command that will return
+# the file/directory group to the pre-Bastille state.
+#
+# Unlike Perl, we've broken the chown function into B_chown/B_chgrp to
+# make error checking simpler.
+#
+# As of 1.2.0, this now supports file globbing. JJB
+#
+##########################################################################
+
+
+sub B_chgrp($$) {
+ my ($newgown,$file_expr)=@_;
+ my $oldown;
+ my $oldgown;
+
+ my $retval=1;
+
+ my $file;
+ my @files = glob($file_expr);
+
+ foreach $file (@files) {
+
+ # Prepend global prefix, but save original filename for &B_backup_file
+ my $original_file=$file;
+
+ $oldown=(stat $file)[4];
+ $oldgown=(stat $file)[5];
+
+ &B_log("ACTION", "Change group ownership on $original_file from $oldgown to $newgown\n");
+ &B_log("ACTION", "chown $oldown,$newgown,\"$original_file\";\n");
+ if ( -e $file ) {
+ unless ($GLOBAL_LOGONLY) {
+ # changing the group for the file/directory
+ $retval = chown $oldown,$newgown,$file;
+ if($retval){
+ # if the distribution is HP-UX then the modifications should
+ # also be made to the IPD (installed product database)
+ if(&GetDistro =~ "^HP-UX"){
+ &B_swmodify($file);
+ }
+ &B_revert_log(&getGlobal('BIN', "chgrp") . " $oldgown $file\n");
+ }
+ }
+ unless ($retval) {
+ &B_log("ERROR","Couldn't change ownership to $newgown on file $original_file\n");
+ }
+ }
+ else {
+ &B_log("ERROR","chgrp: File $original_file doesn't exist!\n");
+ $retval=0;
+ }
+ }
+
+ $retval;
+}
+
+###########################################################################
+# &B_chgrp_link just like &B_chgrp but one exception:
+# if the input file is a link
+# it will not change the target's ownship, it only change the link itself's ownship
+###########################################################################
+sub B_chgrp_link($$) {
+ my ($newgown,$file_expr)=@_;
+ my $chgrp = &getGlobal("BIN","chgrp");
+ my @files = glob($file_expr);
+ my $retval=1;
+
+ foreach my $file (@files) {
+ # Prepend prefix, but save original filename
+ my $original_file=$file;
+ my $oldgown=(stat $file)[5];
+
+ &B_log("ACTION","change group ownership on $original_file from $oldgown to $newgown\n");
+ &B_log("ACTION","chgrp -h $newgown \"$original_file\";\n");
+ if ( -e $file ) {
+ unless ($GLOBAL_LOGONLY) {
+ # do not follow link with option -h
+ `$chgrp -h $newgown $file`;
+ $retval = ($? >> 8);
+ if($retval == 0 ){
+ # if the distribution is HP-UX then the modifications should
+ # also be made to the IPD (installed product database)
+ if(&GetDistro =~ "^HP-UX"){
+ &B_swmodify($file);
+ }
+ # making ownership change revert-able
+ &B_revert_log("$chgrp" . " -h $oldgown $file\n");
+ }
+ }
+ unless (! $retval) {
+ &B_log("ERROR","Couldn't change group ownership to $newgown on file $original_file\n");
+ }
+ }
+ else {
+ &B_log("ERROR","chgrp: File $original_file doesn't exist!\n");
+ $retval=0;
+ }
+ }
+}
+
+###########################################################################
+# B_userdel($user) removes $user from the system, chmoding her home
+# directory to 000, root:root owned, and removes the user from all
+# /etc/passwd, /etc/shadow and /etc/group lines.
+#
+# In the future, we may also choose to make a B_lock_account routine.
+#
+# This routine depends on B_remove_user_from_group.
+###########################################################################
+
+sub B_userdel($) {
+
+ my $user_to_remove = $_[0];
+
+ if (&GetDistro =~ /^HP-UX/) {
+ return 0;
+
+ # Not yet suported on HP-UX, where we'd need to support
+ # the TCB files and such.
+ }
+
+ #
+ # First, let's chmod/chown/chgrp the user's home directory.
+ #
+
+ # Get the user's home directory from /etc/passwd
+ if (open PASSWD,&getGlobal('FILE','passwd')) {
+ my @lines=<PASSWD>;
+ close PASSWD;
+
+ # Get the home directory
+ my $user_line = grep '^\s*$user_to_remove\s*:',@lines;
+ my $home_directory = (split /\s*:\s*/,$user_line)[5];
+
+ # Chmod that home dir to 0000,owned by uid 0, gid 0.
+ if (&B_chmod_if_exists(0000,$home_directory)) {
+ &B_chown(0,$home_directory);
+ &B_chgrp(0,$home_directory);
+ }
+ }
+ else {
+ &B_log('ERROR',"B_userdel couldn't open the passwd file to remove a user.");
+ return 0;
+ }
+
+ #
+ # Next find out what groups the user is in, so we can call
+ # B_remove_user_from_group($user,$group)
+ #
+ # TODO: add this to the helper functions for the test suite.
+ #
+
+ my @groups = ();
+
+ # Parse /etc/group, looking for our user.
+ if (open GROUP,&getGlobal('FILE','group')) {
+ my @lines = <GROUP>;
+ close GROUP;
+
+ foreach my $line (@lines) {
+
+ # Parse the line -- first field is group, last is users in group.
+ if ($line =~ /([^\#^:]+):[^:]+:[^:]+:(.*)/) {
+ my $group = $1;
+ my $users_section = $2;
+
+ # Get the user list and check if our user is in it.
+ my @users = split /\s*,\s*/,$users_section;
+ foreach my $user (@users) {
+ if ($user_to_remove eq $user) {
+ push @groups,$group;
+ last;
+ }
+ }
+ }
+ }
+ }
+
+ # Now remove the user from each of those groups.
+ foreach my $group (@groups) {
+ &B_remove_user_from_group($user_to_remove,$group);
+ }
+
+ # Remove the user's /etc/passwd and /etc/shadow lines
+ &B_delete_line(&getGlobal('FILE','passwd'),"^$user_to_remove\\s*:");
+ &B_delete_line(&getGlobal('FILE','shadow'),"^$user_to_remove\\s*:");
+
+
+ #
+ # We should delete the user's group as well, if it's a single-user group.
+ #
+ if (open ETCGROUP,&getGlobal('FILE','group')) {
+ my @group_lines = <ETCGROUP>;
+ close ETCGROUP;
+ chomp @group_lines;
+
+ if (grep /^$user_to_remove\s*:[^:]*:[^:]*:\s*$/,@group_lines > 0) {
+ &B_groupdel($user_to_remove);
+ }
+ }
+
+}
+
+###########################################################################
+# B_groupdel($group) removes $group from /etc/group.
+###########################################################################
+
+sub B_groupdel($) {
+
+ my $group = $_[0];
+
+ # First read /etc/group to make sure the group is in there.
+ if (open GROUP,&getGlobal('FILE','group')) {
+ my @lines=<GROUP>;
+ close GROUP;
+
+ # Delete the line in /etc/group if present
+ if (grep /^$group:/,@lines > 0) {
+ # The group is named in /etc/group
+ &B_delete_line(&getGlobal('FILE','group'),"^$group:/");
+ }
+ }
+
+}
+
+
+###########################################################################
+# B_remove_user_from_group($user,$group) removes $user from $group,
+# by modifying $group's /etc/group line, pulling the user out. This
+# uses B_chunk_replace thrice to replace these patterns:
+#
+# ":\s*$user\s*," --> ":"
+# ",\s*$user" -> ""
+#
+###########################################################################
+
+sub B_remove_user_from_group($$) {
+
+ my ($user_to_remove,$group) = @_;
+
+ #
+ # We need to find the line from /etc/group that defines the group, parse
+ # it, and put it back together without this user.
+ #
+
+ # Open the group file
+ unless (open GROUP,&getGlobal('FILE','group')) {
+ &B_log('ERROR',"&B_remove_user_from_group couldn't read /etc/group to remove $user_to_remove from $group.\n");
+ return 0;
+ }
+ my @lines = <GROUP>;
+ close GROUP;
+ chomp @lines;
+
+ #
+ # Read through the lines to find the one we care about. We'll construct a
+ # replacement and then use B_replace_line to make the switch.
+ #
+
+ foreach my $line (@lines) {
+
+ if ($line =~ /^\s*$group\s*:/) {
+
+ # Parse this line.
+ my @group_entries = split ':',$line;
+ my @users = split ',',($group_entries[3]);
+
+ # Now, recreate it.
+ my $first_user = 1;
+ my $group_line = $group_entries[0] . ':' . $group_entries[1] . ':' . $group_entries[2] . ':';
+
+ # Add every user except the one we're removing.
+ foreach my $user (@users) {
+
+ # Remove whitespace.
+ $user =~ s/\s+//g;
+
+ if ($user ne $user_to_remove) {
+ # Add the user to the end of the line, prefacing
+ # it with a comma if it's not the first user.
+
+ if ($first_user) {
+ $group_line .= "$user";
+ $first_user = 0;
+ }
+ else {
+ $group_line .= ",$user";
+ }
+ }
+ }
+
+ # The line is now finished. Replace the original line.
+ $group_line .= "\n";
+ &B_replace_line(&getGlobal('FILE','group'),"^\\s*$group\\s*:",$group_line);
+ }
+
+ }
+ return 1;
+}
+
+###########################################################################
+# &B_check_owner_group($$$)
+#
+# Checks if the given file has the given owner and/or group.
+# If the given owner is "", checks group only.
+# If the given group is "", checks owner only.
+#
+# return values:
+# 1: file has the given owner and/or group
+# or file exists, and both the given owner and group are ""
+# 0: file does not has the given owner or group
+# or file does not exists
+############################################################################
+
+sub B_check_owner_group ($$$){
+ my ($fileName, $owner, $group) = @_;
+
+ if (-e $fileName) {
+ my @junk=stat ($fileName);
+ my $uid=$junk[4];
+ my $gid=$junk[5];
+
+ # Check file owner
+ if ($owner ne "") {
+ if (getpwnam($owner) != $uid) {
+ return 0;
+ }
+ }
+
+ # Check file group
+ if ($group ne "") {
+ if (getgrnam($group) != $gid) {
+ return 0;
+ }
+ }
+
+ return 1;
+ }
+ else {
+ # Something is wrong if the file not exist
+ return 0;
+ }
+}
+
+##########################################################################
+# this subroutine will test whether the given file is unowned
+##########################################################################
+sub B_is_unowned_file($) {
+ my $file =$_;
+ my $uid = (stat($file))[4];
+ my $uname = (getpwuid($uid))[0];
+ if ( $uname =~ /.+/ ) {
+ return 1;
+ }
+ return 0;
+}
+
+##########################################################################
+# this subroutine will test whether the given file is ungrouped
+##########################################################################
+sub B_is_ungrouped_file($){
+ my $file =$_;
+ my $gid = (stat($file))[5];
+ my $gname = (getgrgid($gid))[0];
+ if ( $gname =~ /.+/ ) {
+ return 1;
+ }
+ return 0;
+}
+
+
+
+
+###########################################################################
+# &B_check_permissions($$)
+#
+# Checks if the given file has the given permissions or stronger, where we
+# define stronger as "less accessible." The file argument must be fully
+# qualified, i.e. contain the absolute path.
+#
+# return values:
+# 1: file has the given permissions or better
+# 0: file does not have the given permsssions
+# undef: file permissions cannot be determined
+###########################################################################
+
+sub B_check_permissions ($$){
+ my ($fileName, $reqdPerms) = @_;
+ my $filePerms; # actual permissions
+
+
+ if (-e $fileName) {
+ if (stat($fileName)) {
+ $filePerms = (stat($fileName))[2] & 07777;
+ }
+ else {
+ &B_log ("ERROR", "Can't stat $fileName.\n");
+ return undef;
+ }
+ }
+ else {
+ # If the file does not exist, permissions are as good as they can get.
+ return 1;
+ }
+
+ #
+ # We can check whether the $filePerms are as strong by
+ # bitwise ANDing them with $reqdPerms and checking if the
+ # result is still equal to $filePerms. If it is, the
+ # $filePerms are strong enough.
+ #
+ if ( ($filePerms & $reqdPerms) == $filePerms ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+
+}
+
+##########################################################################
+# B_permission_test($user, $previlege,$file)
+# $user can be
+# "owner"
+# "group"
+# "other"
+# $previlege can be:
+# "r"
+# "w"
+# "x"
+# "suid"
+# "sgid"
+# "sticky"
+# if previlege is set to suid or sgid or sticky, then $user can be empty
+# this sub routine test whether the $user has the specified previlige to $file
+##########################################################################
+
+sub B_permission_test($$$){
+ my ($user, $previlege, $file) = @_;
+
+ if (-e $file ) {
+ my $mode = (stat($file))[2];
+ my $bitpos;
+ # bitmap is | suid sgid sticky | rwx | rwx | rwx
+ if ($previlege =~ /suid/ ) {
+ $bitpos = 11;
+ }
+ elsif ($previlege =~ /sgid/ ) {
+ $bitpos = 10;
+ }
+ elsif ($previlege =~ /sticky/ ) {
+ $bitpos = 9;
+ }
+ else {
+ if ( $user =~ /owner/) {
+ if ($previlege =~ /r/) {
+ $bitpos = 8;
+ }
+ elsif ($previlege =~ /w/) {
+ $bitpos =7;
+ }
+ elsif ($previlege =~ /x/) {
+ $bitpos =6;
+ }
+ else {
+ return 0;
+ }
+ }
+ elsif ( $user =~ /group/) {
+ if ($previlege =~ /r/) {
+ $bitpos =5;
+ }
+ elsif ($previlege =~ /w/) {
+ $bitpos =4;
+ }
+ elsif ($previlege =~ /x/) {
+ $bitpos =3;
+ }
+ else {
+ return 0;
+ }
+ }
+ elsif ( $user =~ /other/) {
+ if ($previlege =~ /r/) {
+ $bitpos =2;
+ }
+ elsif ($previlege =~ /w/) {
+ $bitpos =1;
+ }
+ elsif ($previlege =~ /x/) {
+ $bitpos =0;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+ }
+ $mode /= 2**$bitpos;
+ if ($mode % 2) {
+ return 1;
+ }
+ return 0;
+ }
+}
+
+##########################################################################
+# this subroutine will return a list of home directory
+##########################################################################
+sub B_find_homes(){
+ # find loginable homes
+ my $logins = &getGlobal("BIN","logins");
+ my @lines = `$logins -ox`;
+ my @homes;
+ foreach my $line (@lines) {
+ chomp $line;
+ my @data = split /:/, $line;
+ if ($data[7] =~ /PS/ && $data[5] =~ /home/) {
+ push @homes, $data[5];
+ }
+ }
+ return @homes;
+}
+
+
+###########################################################################
+# B_is_executable($)
+#
+# This routine reports on whether a file is executable by the current
+# process' effective UID.
+#
+# scalar return values:
+# 0: file is not executable
+# 1: file is executable
+#
+###########################################################################
+
+sub B_is_executable($)
+{
+ my $name = shift;
+ my $executable = 0;
+
+ if (-x $name) {
+ $executable = 1;
+ }
+ return $executable;
+}
+
+###########################################################################
+# B_is_suid($)
+#
+# This routine reports on whether a file is Set-UID and owned by root.
+#
+# scalar return values:
+# 0: file is not SUID root
+# 1: file is SUID root
+#
+###########################################################################
+
+sub B_is_suid($)
+{
+ my $name = shift;
+
+ my @FileStatus = stat($name);
+ my $IsSuid = 0;
+
+ if (-u $name) #Checks existence and suid
+ {
+ if($FileStatus[4] == 0) {
+ $IsSuid = 1;
+ }
+ }
+
+ return $IsSuid;
+}
+
+###########################################################################
+# B_is_sgid($)
+#
+# This routine reports on whether a file is SGID and group owned by
+# group root (gid 0).
+#
+# scalar return values:
+# 0: file is not SGID root
+# 1: file is SGID root
+#
+###########################################################################
+
+sub B_is_sgid($)
+{
+ my $name = shift;
+
+ my @FileStatus = stat($name);
+ my $IsSgid = 0;
+
+ if (-g $name) #checks existence and sgid
+ {
+ if($FileStatus[5] == 0) {
+ $IsSgid = 1;
+ }
+ }
+
+ return $IsSgid;
+}
+
+###########################################################################
+# B_get_user_list()
+#
+# This routine outputs a list of users on the system.
+#
+###########################################################################
+
+sub B_get_user_list()
+{
+ my @users;
+ open(PASSWD,&getGlobal('FILE','passwd'));
+ while(<PASSWD>) {
+ #Get the users
+ if (/^([^:]+):/)
+ {
+ push (@users,$1);
+ }
+ }
+ return @users;
+}
+
+###########################################################################
+# B_get_group_list()
+#
+# This routine outputs a list of groups on the system.
+#
+###########################################################################
+
+sub B_get_group_list()
+{
+ my @groups;
+ open(GROUP,&getGlobal('FILE','group'));
+ while(my $group_line = <GROUP>) {
+ #Get the groups
+ if ($group_line =~ /^([^:]+):/)
+ {
+ push (@groups,$1);
+ }
+ }
+ return @groups;
+}
+
+
+###########################################################################
+# &B_remove_suid ($file) removes the suid bit from $file if it
+# is set and the file exist. If you would like to remove the suid bit
+# from /bin/ping then you need to use:
+#
+# &B_remove_suid("/bin/ping");
+#
+# &B_remove_suid respects GLOBAL_LOGONLY.
+# &B_remove_suid uses &B_chmod to make the permission changes
+# &B_remove_suid allows for globbing. tyler_e
+#
+###########################################################################
+
+sub B_remove_suid($) {
+ my $file_expr = $_[0];
+
+ &B_log("ACTION","Removing SUID bit from \"$file_expr\".");
+ unless ($GLOBAL_LOGONLY) {
+ my @files = glob($file_expr);
+
+ foreach my $file (@files) {
+ # check file existence
+ if(-e $file){
+ # stat current file to get raw permissions
+ my $old_perm_raw = (stat $file)[2];
+ # test to see if suidbit is set
+ my $suid_bit = (($old_perm_raw/2048) % 2);
+ if($suid_bit == 1){
+ # new permission without the suid bit
+ my $new_perm = ((($old_perm_raw/512) % 8 ) - 4) .
+ (($old_perm_raw/64) % 8 ) .
+ (($old_perm_raw/8) % 8 ) .
+ (($old_perm_raw) % 8 );
+ if(&B_chmod(oct($new_perm), $file)){
+ &B_log("ACTION","Removed SUID bit from \"$file\".");
+ }
+ else {
+ &B_log("ERROR","Could not remove SUID bit from \"$file\".");
+ }
+ } # No action if SUID bit is not set
+ }# No action if file does not exist
+ }# Repeat for each file in the file glob
+ } # unless Global_log
+}
+
+
+
+1;
+
diff --git a/recipes-security/bastille/files/Curses-and-IOLoader-changes.patch b/recipes-security/bastille/files/Curses-and-IOLoader-changes.patch
new file mode 100644
index 0000000..1fb2235
--- /dev/null
+++ b/recipes-security/bastille/files/Curses-and-IOLoader-changes.patch
@@ -0,0 +1,51 @@
+From 456daee3ce57d3a46bf9ccf0a85ec4880ca5b262 Mon Sep 17 00:00:00 2001
+From: Andrei Dinu <andrei.adrianx.dinu@intel.com>
+Date: Tue, 4 Jun 2013 14:56:21 +0300
+Subject: [PATCH] Curses and IOLoader changes
+
+The linux distribution couldn't be identified when
+running Bastille, and the question pruning method
+couldn't get a match on the questions relevant to
+the repo, so it eliminated all quetions.
+
+After answering the questions the checkAndSaveConfig routine
+was called which was missing. Replaced it with Run_Bastille_
+with_Config which exists.
+
+Signed-off-by: Andrei Dinu <andrei.adrianx.dinu@intel.com>
+---
+ Bastille/IOLoader.pm | 2 +-
+ Bastille_Curses.pm | 4 +++-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/Bastille/IOLoader.pm b/Bastille/IOLoader.pm
+index abb94d7..995d2c2 100644
+--- a/Bastille/IOLoader.pm
++++ b/Bastille/IOLoader.pm
+@@ -68,7 +68,7 @@ sub Load_Questions($) {
+ my $UseRequiresRules = $_[0];
+
+ my ($current_module_number,$first_question) = &parse_questions();
+- $first_question = &prune_questions($UseRequiresRules,$first_question);
++ #$first_question = &prune_questions($UseRequiresRules,$first_question);
+ $firstQuestion = $first_question;
+ &B_log("DEBUG","Load Questions, first question: $first_question");
+ &validate_questions();
+diff --git a/Bastille_Curses.pm b/Bastille_Curses.pm
+index 2e1eef4..edbbe45 100644
+--- a/Bastille_Curses.pm
++++ b/Bastille_Curses.pm
+@@ -84,7 +84,9 @@ sub do_Bastille {
+ }
+
+ # Output answers to the script and display
+- &checkAndSaveConfig(&getGlobal('BFILE', "config"));
++ #&checkAndSaveConfig(&getGlobal('BFILE', "config"));
++
++ &Run_Bastille_with_Config;
+
+ # Run Bastille
+
+--
+1.7.9.5
+
diff --git a/recipes-security/bastille/files/FileContent.pm b/recipes-security/bastille/files/FileContent.pm
new file mode 100644
index 0000000..0a5d609
--- /dev/null
+++ b/recipes-security/bastille/files/FileContent.pm
@@ -0,0 +1,1153 @@
+package Bastille::API::FileContent;
+use strict;
+
+use Bastille::API;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(
+B_blank_file
+B_insert_line_after
+B_insert_line_before
+B_insert_line
+B_append_line
+B_prepend_line
+B_replace_line
+B_replace_lines
+B_replace_pattern
+B_match_line
+B_match_line_only
+B_match_chunk
+B_return_matched_lines
+B_hash_comment_line
+B_hash_uncomment_line
+B_delete_line
+B_chunk_replace
+B_print
+B_getValueFromFile
+B_getValueFromString
+
+B_TODO
+B_TODOFlags
+);
+our @EXPORT = @EXPORT_OK;
+
+
+
+###########################################################################
+# &B_blank_file ($filename,$pattern) blanks the file $filename, unless the
+# pattern $pattern is present in the file. This lets us completely redo
+# a file, if it isn't the one we put in place on a previous run...
+#
+# B_blank_file respects $GLOBAL_LOGONLY and uses B_open_plus and B_close_plus
+# so that it makes backups and only modifies files when we're not in "-v"
+# mode...
+#
+# If the file does not exist, the function does nothing, and gives an error
+# to the Error Log
+#
+###########################################################################
+
+sub B_blank_file($$) {
+
+ my ($filename,$pattern) = @_;
+ my $retval;
+
+ # If this variable is true, we won't blank the file...
+
+ my $found_pattern=0;
+
+ if ($retval=&B_open_plus (*BLANK_NEW,*BLANK_OLD,$filename) ) {
+
+ my @lines;
+
+ while (my $line = <BLANK_OLD>) {
+
+ push @lines,$line;
+ if ($line =~ $pattern) {
+ $found_pattern=1;
+ }
+ }
+
+ # Only copy the old file if the new one didn't match.
+ if ($found_pattern) {
+ while ( my $line = shift @lines ) {
+ &B_print(*BLANK_NEW,$line);
+ }
+ }
+ else {
+ &B_log("ACTION","Blanked file $filename\n");
+ }
+ &B_close_plus(*BLANK_NEW,*BLANK_OLD,$filename);
+ }
+ else {
+ &B_log("ERROR","Couldn't blank file $filename since we couldn't open it or its replacement\n");
+ }
+
+ return $retval;
+
+}
+
+###########################################################################
+# &B_insert_line_after ($filename,$pattern,$line_to_insert,$line_to_follow)
+# modifies $filename, inserting $line_to_insert unless one or more lines
+# in the file matches $pattern. The $line_to_insert will be placed
+# immediately after $line_to_follow, if it exists. If said line does not
+# exist, the line will not be inserted and this routine will return 0.
+#
+# B_insert_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here's examples of where you might use this:
+#
+# You'd like to insert a line in Apache's configuration file, in a
+# particular section.
+#
+###########################################################################
+
+sub B_insert_line_after($$$$) {
+
+ my ($filename,$pattern,$line_to_insert,$line_to_follow) = @_;
+
+ my @lines;
+ my $found_pattern=0;
+ my $found_line_to_follow=0;
+
+ my $retval=1;
+
+ if ( &B_open_plus (*INSERT_NEW,*INSERT_OLD,$filename) ) {
+
+ # Read through the file looking for a match both on the $pattern
+ # and the line we are supposed to be inserting after...
+
+ my $ctr=1;
+ while (my $line=<INSERT_OLD>) {
+ push (@lines,$line);
+ if ($line =~ $pattern) {
+ $found_pattern=1;
+ }
+ if ( ($found_line_to_follow < 1) and ($line =~ $line_to_follow)) {
+ $found_line_to_follow=$ctr;
+ }
+ $ctr++;
+ }
+
+ # Log an error if we never found the line we were to insert after
+ unless ($found_line_to_follow ) {
+ $retval=0;
+ &B_log("ERROR","Never found the line that we were supposed to insert after in $filename\n");
+ }
+
+ # Now print the file back out, inserting our line if we should...
+
+ $ctr=1;
+ while (my $line = shift @lines) {
+ &B_print(*INSERT_NEW,$line);
+ if ( ($ctr == $found_line_to_follow) and ($found_pattern == 0) ) {
+ &B_print(*INSERT_NEW,$line_to_insert);
+ &B_log("ACTION","Inserted the following line in $filename:\n");
+ &B_log("ACTION","$line_to_insert");
+ }
+ $ctr++;
+ }
+
+ &B_close_plus (*INSERT_NEW,*INSERT_OLD,$filename);
+
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't insert line to $filename, since open failed.");
+ }
+
+ return $retval;
+
+}
+###########################################################################
+# &B_insert_line_before ($filename,$pattern,$line_to_insert,$line_to_preceed)
+# modifies $filename, inserting $line_to_insert unless one or more lines
+# in the file matches $pattern. The $line_to_insert will be placed
+# immediately before $line_to_preceed, if it exists. If said line does not
+# exist, the line will not be inserted and this routine will return 0.
+#
+# B_insert_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here's examples of where you might use this:
+#
+# You'd like to insert a line in Apache's configuration file, in a
+# particular section.
+#
+###########################################################################
+
+sub B_insert_line_before($$$$) {
+
+ my ($filename,$pattern,$line_to_insert,$line_to_preceed) = @_;
+
+ my @lines;
+ my $found_pattern=0;
+ my $found_line_to_preceed=0;
+
+ my $retval=1;
+
+ if ( &B_open_plus (*INSERT_NEW,*INSERT_OLD,$filename) ) {
+
+ # Read through the file looking for a match both on the $pattern
+ # and the line we are supposed to be inserting after...
+
+ my $ctr=1;
+ while (my $line=<INSERT_OLD>) {
+ push (@lines,$line);
+ if ($line =~ $pattern) {
+ $found_pattern=1;
+ }
+ if ( ($found_line_to_preceed < 1) and ($line =~ $line_to_preceed)) {
+ $found_line_to_preceed=$ctr;
+ }
+ $ctr++;
+ }
+
+ # Log an error if we never found the line we were to preceed
+ unless ($found_line_to_preceed ) {
+ $retval=0;
+ &B_log("ERROR","Never found the line that we were supposed to insert before in $filename\n");
+ }
+
+ # Now print the file back out, inserting our line if we should...
+
+ $ctr=1;
+ while (my $line = shift @lines) {
+ if ( ($ctr == $found_line_to_preceed) and ($found_pattern == 0) ) {
+ &B_print(*INSERT_NEW,$line_to_insert);
+ &B_log("ACTION","Inserted the following line in $filename:\n");
+ &B_log("ACTION","$line_to_insert");
+ }
+ &B_print(*INSERT_NEW,$line);
+ $ctr++;
+ }
+
+ &B_close_plus (*INSERT_NEW,*INSERT_OLD,$filename);
+
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't insert line to $filename, since open failed.");
+ }
+
+ return $retval;
+
+}
+
+###########################################################################
+# &B_insert_line ($filename,$pattern,$line_to_insert,$line_to_follow)
+#
+# has been renamed to B_insert_line_after()
+#
+# This name will continue to work, as a shim for code that has not been
+# transitioned.
+###########################################################################
+
+sub B_insert_line($$$$) {
+
+ my $rtn_value = &B_insert_line_after(@_);
+
+ return ($rtn_value);
+}
+
+
+###########################################################################
+# &B_append_line ($filename,$pattern,$line_to_append) modifies $filename,
+# appending $line_to_append unless one or more lines in the file matches
+# $pattern. This is an enhancement to the append_line_if_no_such_line_exists
+# idea.
+#
+# Additionally, if $pattern is set equal to "", the line is always appended.
+#
+# B_append_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here's examples of where you might use this:
+#
+# You'd like to add a root line to /etc/ftpusers if none exists.
+# You'd like to add a Options Indexes line to Apache's config. file,
+# after you delete all Options lines from said config file.
+#
+###########################################################################
+
+sub B_append_line($$$) {
+
+ my ($filename,$pattern,$line_to_append) = @_;
+
+ my $found_pattern=0;
+ my $retval=1;
+
+ if ( &B_open_plus (*APPEND_NEW,*APPEND_OLD,$filename) ) {
+ while (my $line=<APPEND_OLD>) {
+ &B_print(*APPEND_NEW,$line);
+ if ($line =~ $pattern) {
+ $found_pattern=1;
+ }
+ }
+ # Changed != 0 to $pattern so that "" works instead of 0 and perl
+ # does not give the annoying
+ # Argument "XX" isn't numeric in ne at ...
+ if ( $pattern eq "" or ! $found_pattern ) {
+ &B_print(*APPEND_NEW,$line_to_append);
+ &B_log("ACTION","Appended the following line to $filename:\n");
+ &B_log("ACTION","$line_to_append");
+ }
+ &B_close_plus (*APPEND_NEW,*APPEND_OLD,$filename);
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","# Couldn't append line to $filename, since open failed.");
+ }
+
+ return $retval;
+
+}
+
+###########################################################################
+# &B_prepend_line ($filename,$pattern,$line_to_prepend) modifies $filename,
+# pre-pending $line_to_prepend unless one or more lines in the file matches
+# $pattern. This is an enhancement to the prepend_line_if_no_such_line_exists
+# idea.
+#
+# B_prepend_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here's examples of where you might use this:
+#
+# You'd like to insert the line "auth required pam_deny.so" to the top
+# of the PAM stack file /etc/pam.d/rsh to totally deactivate rsh.
+#
+###########################################################################
+
+sub B_prepend_line($$$) {
+
+ my ($filename,$pattern,$line_to_prepend) = @_;
+
+ my @lines;
+ my $found_pattern=0;
+ my $retval=1;
+
+ if ( &B_open_plus (*PREPEND_NEW,*PREPEND_OLD,$filename) ) {
+ while (my $line=<PREPEND_OLD>) {
+ push (@lines,$line);
+ if ($line =~ $pattern) {
+ $found_pattern=1;
+ }
+ }
+ unless ($found_pattern) {
+ &B_print(*PREPEND_NEW,$line_to_prepend);
+ }
+ while (my $line = shift @lines) {
+ &B_print(*PREPEND_NEW,$line);
+ }
+
+ &B_close_plus (*PREPEND_NEW,*PREPEND_OLD,$filename);
+
+ # Log the action
+ &B_log("ACTION","Pre-pended the following line to $filename:\n");
+ &B_log("ACTION","$line_to_prepend");
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't prepend line to $filename, since open failed.\n");
+ }
+
+ return $retval;
+
+}
+
+
+###########################################################################
+# &B_replace_line ($filename,$pattern,$line_to_switch_in) modifies $filename,
+# replacing any lines matching $pattern with $line_to_switch_in.
+#
+# It returns the number of lines it replaced (or would have replaced, if
+# LOGONLY mode wasn't on...)
+#
+# B_replace_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here an example of where you might use this:
+#
+# You'd like to replace any Options lines in Apache's config file with:
+# Options Indexes FollowSymLinks
+#
+###########################################################################
+
+sub B_replace_line($$$) {
+
+ my ($filename,$pattern,$line_to_switch_in) = @_;
+ my $retval=0;
+
+ if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) {
+ while (my $line=<REPLACE_OLD>) {
+ unless ($line =~ $pattern) {
+ &B_print(*REPLACE_NEW,$line);
+ }
+ else {
+ # Don't replace the line if it's already there.
+ unless ($line eq $line_to_switch_in) {
+ &B_print(*REPLACE_NEW,$line_to_switch_in);
+
+ $retval++;
+ &B_log("ACTION","File modification in $filename -- replaced line\n" .
+ "$line\n" .
+ "with:\n" .
+ "$line_to_switch_in");
+ }
+ # But if it is there, make sure it stays there! (by Paul Allen)
+ else {
+ &B_print(*REPLACE_NEW,$line);
+ }
+ }
+ }
+ &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename);
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't replace line(s) in $filename because open failed.\n");
+ }
+
+ return $retval;
+}
+
+###########################################################################
+# &B_replace_lines ($filename,$patterns_and_substitutes) modifies $filename,
+# replacing the line matching the nth $pattern specified in $patterns_and_substitutes->[n]->[0]
+# with the corresponding substitutes in $patterns_and_substitutes->[n]->-[1]
+#
+# It returns the number of lines it replaced (or would have replaced, if
+# LOGONLY mode wasn't on...)
+#
+# B_replace_lines uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here an example of where you might use this:
+#
+# You'd like to replace /etc/opt/ssh/sshd_config file
+# (^#|^)Protocol\s+(.*)\s*$ ==> Protocol 2
+# (^#|^)X11Forwarding\s+(.*)\s*$ ==> X11Forwarding yes
+# (^#|^)IgnoreRhosts\s+(.*)\s*$ ==> gnoreRhosts yes
+# (^#|^)RhostsAuthentication\s+(.*)\s*$ ==> RhostsAuthentication no
+# (^#|^)RhostsRSAAuthentication\s+(.*)\s*$ ==> RhostsRSAAuthentication no
+# (^#|^)PermitRootLogin\s+(.*)\s*$ ==> PermitRootLogin no
+# (^#|^)PermitEmptyPasswords\s+(.*)\s*$ ==> PermitEmptyPasswords no
+# my $patterns_and_substitutes = [
+# [ '(^#|^)Protocol\s+(.*)\s*$' => 'Protocol 2'],
+# ['(^#|^)X11Forwarding\s+(.*)\s*$' => 'X11Forwarding yes'],
+# ['(^#|^)IgnoreRhosts\s+(.*)\s*$' => 'gnoreRhosts yes'],
+# ['(^#|^)RhostsAuthentication\s+(.*)\s*$' => 'RhostsAuthentication no'],
+# ['(^#|^)RhostsRSAAuthentication\s+(.*)\s*$' => 'RhostsRSAAuthentication no'],
+# ['(^#|^)PermitRootLogin\s+(.*)\s*$' => 'PermitRootLogin no'],
+# ['(^#|^)PermitEmptyPasswords\s+(.*)\s*$' => 'PermitEmptyPasswords no']
+#]
+# B_replaces_lines($sshd_config,$patterns_and_substitutes);
+###########################################################################
+
+sub B_replace_lines($$){
+ my ($filename, $pairs) = @_;
+ my $retval = 0;
+ if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) {
+ while (my $line = <REPLACE_OLD>) {
+ my $switch;
+ my $switch_before = $line;
+ chomp($line);
+ foreach my $pair (@$pairs) {
+ $switch = 0;
+
+ my $pattern = $pair->[0] ;
+ my $replace = $pair->[1];
+ my $evalstr = '$line' . "=~ s/$pattern/$replace/";
+ eval $evalstr;
+ if ($@) {
+ &B_log("ERROR", "eval $evalstr failed.\n");
+ }
+ #if ( $line =~ s/$pair->[0]/$pair->[1]/) {
+ # $switch = 1;
+ # last;
+ #}
+ }
+ &B_print(*REPLACE_NEW,"$line\n");
+ if ($switch) {
+ $retval++;
+ B_log("ACTION","File modification in $filename -- replaced line\n" .
+ "$switch_before\n" .
+ "with:\n" .
+ "$line\n");
+ }
+ }
+ &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename);
+ return 1;
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't replace line(s) in $filename because open failed.\n");
+ }
+}
+
+################################################################################################
+# &B_replace_pattern ($filename,$pattern,$pattern_to_remove,$text_to_switch_in)
+# modifies $filename, acting on only lines that match $pattern, replacing a
+# string that matches $pattern_to_remove with $text_to_switch_in.
+#
+# Ex:
+# B_replace_pattern('/etc/httpd.conf','^\s*Options.*\bIncludes\b','Includes','IncludesNoExec')
+#
+# replaces all "Includes" with "IncludesNoExec" on Apache Options lines.
+#
+# It returns the number of lines it altered (or would have replaced, if
+# LOGONLY mode wasn't on...)
+#
+# B_replace_pattern uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+#################################################################################################
+
+sub B_replace_pattern($$$$) {
+
+ my ($filename,$pattern,$pattern_to_remove,$text_to_switch_in) = @_;
+ my $retval=0;
+
+ if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) {
+ while (my $line=<REPLACE_OLD>) {
+ unless ($line =~ $pattern) {
+ &B_print(*REPLACE_NEW,$line);
+ }
+ else {
+ my $orig_line =$line;
+ $line =~ s/$pattern_to_remove/$text_to_switch_in/;
+
+ &B_print(*REPLACE_NEW,$line);
+
+ $retval++;
+ &B_log("ACTION","File modification in $filename -- replaced line\n" .
+ "$orig_line\n" .
+ "via pattern with:\n" .
+ "$line\n\n");
+ }
+ }
+ &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename);
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't pattern-replace line(s) in $filename because open failed.\n");
+ }
+
+ return $retval;
+}
+
+
+###########################################################################
+# &B_match_line($file,$pattern);
+#
+# This subroutine will return a 1 if the pattern specified can be matched
+# against the file specified. It will return a 0 otherwise.
+#
+# return values:
+# 0: pattern not in file or the file is not readable
+# 1: pattern is in file
+###########################################################################
+sub B_match_line($$) {
+ # file to be checked and pattern to check for.
+ my ($file,$pattern) = @_;
+ # if the file is readable then
+ if(-r $file) {
+ # if the file can be opened then
+ if(open FILE,"<$file") {
+ # look at each line in the file
+ while (my $line = <FILE>) {
+ # if a line matches the pattern provided then
+ if($line =~ $pattern) {
+ # return the pattern was found
+ B_log('DEBUG','Pattern: ' . $pattern . ' matched in file: ' .
+ $file . "\n");
+ return 1;
+ }
+ }
+ }
+ # if the file cann't be opened then
+ else {
+ # send a note to that affect to the errorlog
+ &B_log("ERROR","Unable to open file for read.\n$file\n$!\n");
+ }
+ }
+ B_log('DEBUG','Pattern: ' . $pattern . ' not matched in file: ' .
+ $file . "\n");
+ # the provided pattern was not matched against a line in the file
+ return 0;
+}
+
+###########################################################################
+# &B_match_line_only($file,$pattern);
+#
+# This subroutine checks if the specified pattern can be matched and if
+# it's the only content in the file. The only content means it's only but
+# may have several copies in the file.
+#
+# return values:
+# 0: pattern not in file or pattern is not the only content
+# or the file is not readable
+# 1: pattern is in file and it's the only content
+############################################################################
+sub B_match_line_only($$) {
+ my ($file,$pattern) = @_;
+
+ # if matched, set to 1 later
+ my $retval = 0;
+
+ # if the file is readable then
+ if(-r $file) {
+ # if the file can be opened then
+ if(&B_open(*FILED, $file)) {
+ # pattern should be matched at least once
+ # pattern can not be mismatched
+ while (my $line = <FILED>) {
+ if ($line =~ $pattern) {
+ $retval = 1;
+ }
+ else {
+ &B_close(*FILED);
+ return 0;
+ }
+ }
+ }
+ &B_close(*FILED);
+ }
+
+ return $retval;
+}
+
+###########################################################################
+# &B_return_matched_lines($file,$pattern);
+#
+# This subroutine returns lines in a file matching a given regular
+# expression, when called in the default list mode. When called in scalar
+# mode, returns the number of elements found.
+###########################################################################
+sub B_return_matched_lines($$)
+{
+ my ($filename,$pattern) = @_;
+ my @lines = ();
+
+ open(READFILE, $filename);
+ while (<READFILE>) {
+ chomp;
+ next unless /$pattern/;
+ push(@lines, $_);
+ }
+ if (wantarray)
+ {
+ return @lines;
+ }
+ else
+ {
+ return scalar (@lines);
+ }
+}
+
+###########################################################################
+# &B_match_chunk($file,$pattern);
+#
+# This subroutine will return a 1 if the pattern specified can be matched
+# against the file specified on a line-agnostic form. This allows for
+# patterns which by necessity must match against a multi-line pattern.
+# This is the natural analogue to B_replace_chunk, which was created to
+# provide multi-line capability not provided by B_replace_line.
+#
+# return values:
+# 0: pattern not in file or the file is not readable
+# 1: pattern is in file
+###########################################################################
+
+sub B_match_chunk($$) {
+
+ my ($file,$pattern) = @_;
+ my @lines;
+ my $big_long_line;
+ my $retval=1;
+
+ open CHUNK_FILE,$file;
+
+ # Read all lines into one scalar.
+ @lines = <CHUNK_FILE>;
+ close CHUNK_FILE;
+
+ foreach my $line ( @lines ) {
+ $big_long_line .= $line;
+ }
+
+ # Substitution routines get weird unless last line is terminated with \n
+ chomp $big_long_line;
+ $big_long_line .= "\n";
+
+ # Exit if we don't find a match
+ unless ($big_long_line =~ $pattern) {
+ $retval = 0;
+ }
+
+ return $retval;
+}
+
+###########################################################################
+# &B_hash_comment_line ($filename,$pattern) modifies $filename, replacing
+# any lines matching $pattern with a "hash-commented" version, like this:
+#
+#
+# finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
+# becomes:
+# #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
+#
+# Also:
+# tftp dgram udp wait root /usr/lbin/tftpd tftpd\
+# /opt/ignite\
+# /var/opt/ignite
+# becomes:
+# #tftp dgram udp wait root /usr/lbin/tftpd tftpd\
+# # /opt/ignite\
+# # /var/opt/ignite
+#
+#
+# B_hash_comment_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+###########################################################################
+
+sub B_hash_comment_line($$) {
+
+ my ($filename,$pattern) = @_;
+ my $retval=1;
+
+ if ( &B_open_plus (*HASH_NEW,*HASH_OLD,$filename) ) {
+ my $line;
+ while ($line=<HASH_OLD>) {
+ unless ( ($line =~ $pattern) and ($line !~ /^\s*\#/) ) {
+ &B_print(*HASH_NEW,$line);
+ }
+ else {
+ &B_print(*HASH_NEW,"#$line");
+ &B_log("ACTION","File modification in $filename -- hash commented line\n" .
+ "$line\n" .
+ "like this:\n" .
+ "#$line\n\n");
+ # while the line has a trailing \ then we should also comment out the line below
+ while($line =~ m/\\\n$/) {
+ if($line=<HASH_OLD>) {
+ &B_print(*HASH_NEW,"#$line");
+ &B_log("ACTION","File modification in $filename -- hash commented line\n" .
+ "$line\n" .
+ "like this:\n" .
+ "#$line\n\n");
+ }
+ else {
+ $line = "";
+ }
+ }
+
+ }
+ }
+ &B_close_plus (*HASH_NEW,*HASH_OLD,$filename);
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't hash-comment line(s) in $filename because open failed.\n");
+ }
+
+ return $retval;
+}
+
+
+###########################################################################
+# &B_hash_uncomment_line ($filename,$pattern) modifies $filename,
+# removing any commenting from lines that match $pattern.
+#
+# #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
+# becomes:
+# finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
+#
+#
+# B_hash_uncomment_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+###########################################################################
+
+sub B_hash_uncomment_line($$) {
+
+ my ($filename,$pattern) = @_;
+ my $retval=1;
+
+ if ( &B_open_plus (*HASH_NEW,*HASH_OLD,$filename) ) {
+ my $line;
+ while ($line=<HASH_OLD>) {
+ unless ( ($line =~ $pattern) and ($line =~ /^\s*\#/) ) {
+ &B_print(*HASH_NEW,$line);
+ }
+ else {
+ $line =~ /^\s*\#+(.*)$/;
+ $line = "$1\n";
+
+ &B_print(*HASH_NEW,"$line");
+ &B_log("ACTION","File modification in $filename -- hash uncommented line\n");
+ &B_log("ACTION",$line);
+ # while the line has a trailing \ then we should also uncomment out the line below
+ while($line =~ m/\\\n$/) {
+ if($line=<HASH_OLD>) {
+ $line =~ /^\s*\#+(.*)$/;
+ $line = "$1\n";
+ &B_print(*HASH_NEW,"$line");
+ &B_log("ACTION","File modification in $filename -- hash uncommented line\n");
+ &B_log("ACTION","#$line");
+ &B_log("ACTION","like this:\n");
+ &B_log("ACTION","$line");
+ }
+ else {
+ $line = "";
+ }
+ }
+ }
+ }
+ &B_close_plus (*HASH_NEW,*HASH_OLD,$filename);
+ }
+ else {
+ $retval=0;
+ &B_log("ERROR","Couldn't hash-uncomment line(s) in $filename because open failed.\n");
+ }
+
+ return $retval;
+}
+
+
+
+###########################################################################
+# &B_delete_line ($filename,$pattern) modifies $filename, deleting any
+# lines matching $pattern. It uses B_replace_line to do this.
+#
+# B_replace_line uses B_open_plus and B_close_plus, so that the file
+# modified is backed up...
+#
+# Here an example of where you might use this:
+#
+# You'd like to remove any timeout= lines in /etc/lilo.conf, so that your
+# delay=1 modification will work.
+
+#
+###########################################################################
+
+
+sub B_delete_line($$) {
+
+ my ($filename,$pattern)=@_;
+ my $retval=&B_replace_line($filename,$pattern,"");
+
+ return $retval;
+}
+
+
+###########################################################################
+# &B_chunk_replace ($file,$pattern,$replacement) reads $file replacing the
+# first occurrence of $pattern with $replacement.
+#
+###########################################################################
+
+sub B_chunk_replace($$$) {
+
+ my ($file,$pattern,$replacement) = @_;
+
+ my @lines;
+ my $big_long_line;
+ my $retval=1;
+
+ &B_open (*OLDFILE,$file);
+
+ # Read all lines into one scalar.
+ @lines = <OLDFILE>;
+ &B_close (*OLDFILE);
+ foreach my $line ( @lines ) {
+ $big_long_line .= $line;
+ }
+
+ # Substitution routines get weird unless last line is terminated with \n
+ chomp $big_long_line;
+ $big_long_line .= "\n";
+
+ # Exit if we don't find a match
+ unless ($big_long_line =~ $pattern) {
+ return 0;
+ }
+
+ $big_long_line =~ s/$pattern/$replacement/s;
+
+ $retval=&B_open_plus (*NEWFILE,*OLDFILE,$file);
+ if ($retval) {
+ &B_print (*NEWFILE,$big_long_line);
+ &B_close_plus (*NEWFILE,*OLDFILE,$file);
+ }
+
+ return $retval;
+}
+
+###########################################################################
+# &B_print ($handle,@list) prints the items of @list to the file handle
+# $handle. It logs the action and respects the $GLOBAL_LOGONLY variable.
+#
+###########################################################################
+
+sub B_print {
+ my $handle=shift @_;
+
+ my $result=1;
+
+ unless ($GLOBAL_LOGONLY) {
+ $result=print $handle @_;
+ }
+
+ ($handle) = "$handle" =~ /[^:]+::[^:]+::([^:]+)/;
+
+ $result;
+}
+
+
+##########################################################################
+# &B_getValueFromFile($regex,$file);
+# Takes a regex with a single group "()" and returns the unique value
+# on any non-commented lines
+# This (and B_return_matched_lines are only used in this file, though are
+# probably more generally useful. For now, leaving these here serve the following
+#functions:
+# a) still gets exported/associated as part of the Test_API package, and
+# is still availble for a couple operations that can't be deferred to the
+# main test loop, as they save values so that individual tests don't have to
+# recreate (copy / paste) the logic to get them.
+#
+# It also avoids the circular "use" if we incldued "use Test API" at the top
+# of this file (Test API "uses" this file.
+# Returns the uncommented, unique values of a param=value pair.
+#
+# Return values:
+# 'Not Defined' if the value is not present or not uniquely defined.
+# $value if the value is present and unique
+#
+###########################################################################
+sub B_getValueFromFile ($$){
+ my $inputRegex=$_[0];
+ my $file=$_[1];
+ my ($lastvalue,$value)='';
+
+ my @lines=&B_return_matched_lines($file, $inputRegex);
+
+ return &B_getValueFromString($inputRegex,join('/n',@lines));
+}
+
+##########################################################################
+# &B_getValueFromString($param,$string);
+# Takes a regex with a single group "()" and returns the unique value
+# on any non-commented lines
+# This (and B_return_matched_lines are only used in this file, though are
+# probably more generally useful. For now, leaving these here serve the following
+#functions:
+# a) still gets exported/associated as part of the Test_API package, and
+# is still availble for a couple operations that can't be deferred to the
+# main test loop, as they save values so that individual tests don't have to
+# recreate (copy / paste) the logic to get them.
+#
+# It also avoids the circular "use" if we incldued "use Test API" at the top
+# of this file (Test API "uses" this file.
+# Returns the uncommented, unique values of a param=value pair.
+#
+# Return values:
+# 'Not Unique' if the value is not uniquely defined.
+# undef if the value isn't defined at all
+# $value if the value is present and unique
+#
+###########################################################################
+sub B_getValueFromString ($$){
+ my $inputRegex=$_[0];
+ my $inputString=$_[1];
+ my $lastValue='';
+ my $value='';
+
+ my @lines=split(/\n/,$inputString);
+
+ &B_log("DEBUG","B_getvaluefromstring called with regex: $inputRegex and input: " .
+ $inputString);
+ foreach my $line (grep(/$inputRegex/,@lines)) {
+ $line =~ /$inputRegex/;
+ $value=$1;
+ if (($lastValue eq '') and ($value ne '')) {
+ $lastValue = $value;
+ } elsif (($lastValue ne $value) and ($value ne '')) {
+ B_log("DEBUG","getvaluefromstring returned Not Unique");
+ return 'Not Unique';
+ }
+ }
+ if ((not(defined($value))) or ($value eq '')) {
+ &B_log("DEBUG","Could not find regex match in string");
+ return undef;
+ } else {
+ &B_log("DEBUG","B_getValueFromString Found: $value ; using: $inputRegex");
+ return $value;
+ }
+}
+
+###############################################################
+# This function adds something to the To Do List.
+# Arguments:
+# 1) The string you want to add to the To Do List.
+# 2) Optional: Question whose TODOFlag should be set to indicate
+# A pending manual action in subsequent reports. Only skip this
+# If there's no security-audit relevant action you need the user to
+# accomplish
+# Ex:
+# &B_TODO("------\nInstalling IPFilter\n----\nGo get Ipfilter","IPFilter.install_ipfilter");
+#
+#
+# Returns:
+# 0 - If error condition
+# True, if sucess, specifically:
+# "appended" if the append operation was successful
+# "exists" if no change was made since the entry was already present
+###############################################################
+sub B_TODO ($;$) {
+ my $text = $_[0];
+ my $FlaggedQuestion = $_[1];
+ my $multilineString = "";
+
+ # trim off any leading and trailing new lines, regexes separated for "clarity"
+ $text =~ s/^\n+(.*)/$1/;
+ $text =~ s/(.*)\n+$/$1/;
+
+ if ( ! -e &getGlobal('BFILE',"TODO") ) {
+ # Make the TODO list file for HP-UX Distro
+ &B_create_file(&getGlobal('BFILE', "TODO"));
+ &B_append_line(&getGlobal('BFILE', "TODO"),'a$b',
+ "Please take the steps below to make your system more secure,\n".
+ "then delete the item from this file and record what you did along\n".
+ "with the date and time in your system administration log. You\n".
+ "will need that information in case you ever need to revert your\n".
+ "changes.\n\n");
+ }
+
+
+ if (open(TODO,"<" . &getGlobal('BFILE', "TODO"))) {
+ while (my $line = <TODO>) {
+ # getting rid of all meta characters.
+ $line =~ s/(\\|\||\(|\)|\[|\]|\{|\}|\^|\$|\*|\+|\?|\.)//g;
+ $multilineString .= $line;
+ }
+ chomp $multilineString;
+ $multilineString .= "\n";
+
+ close(TODO);
+ }
+ else {
+ &B_log("ERROR","Unable to read TODO.txt file.\n" .
+ "The following text could not be appended to the TODO list:\n" .
+ $text .
+ "End of TODO text\n");
+ return 0; #False
+ }
+
+ my $textPattern = $text;
+
+ # getting rid of all meta characters.
+ $textPattern =~ s/(\\|\||\(|\)|\[|\]|\{|\}|\^|\$|\*|\+|\?|\.)//g;
+
+ if( $multilineString !~ "$textPattern") {
+ my $datestamp = "{" . localtime() . "}";
+ unless ( &B_append_line(&getGlobal('BFILE', "TODO"), "", $datestamp . "\n" . $text . "\n\n\n") ) {
+ &B_log("ERROR","TODO Failed for text: " . $text );
+ }
+ #Note that we only set the flag on the *initial* entry in the TODO File
+ #Not on subsequent detection. This is to avoid the case where Bastille
+ #complains on a subsequent Bastille run of an already-performed manual
+ #action that the user neglected to delete from the TODO file.
+ # It does, however lead to a report of "nonsecure" when the user
+ #asked for the TODO item, performed it, Bastille detected that and cleared the
+ # Item, and then the user unperformed the action. I think this is proper behavior.
+ # rwf 06/06
+
+ if (defined($FlaggedQuestion)) {
+ &B_TODOFlags("set",$FlaggedQuestion);
+ }
+ return "appended"; #evals to true, and also notes what happened
+ } else {
+ return "exists"; #evals to true, and also
+ }
+
+}
+
+
+#####################################################################
+# &B_TODOFlags()
+#
+# This is the interface to the TODO flags. Test functions set these when they
+# require a TODO item to be completed to get to a "secure" state.
+# The prune/reporting function checks these to ensure no flags are set before
+# reporting an item "secure"
+# "Methods" are load | save | isSet <Question> | set <Question> | unset <Question>
+#
+######################################################################
+
+sub B_TODOFlags($;$) {
+ my $action = $_[0];
+ my $module = $_[1];
+
+ use File::Spec;
+
+ my $todo_flag = &getGlobal("BFILE","TODOFlag");
+
+ &B_log("DEBUG","B_TODOFlags action: $action , module: $module");
+
+ if ($action eq "load") {
+ if (-e $todo_flag ) {
+ &B_open(*TODO_FLAGS, $todo_flag);
+ my @lines = <TODO_FLAGS>;
+ foreach my $line (@lines) {
+ chomp($line);
+ $GLOBAL_CONFIG{"$line"}{"TODOFlag"}="yes";
+ }
+ return (&B_close(*TODO_FLAGS)); #return success of final close
+ } else {
+ return 1; #No-op is okay
+ }
+ } elsif ($action eq "save") {
+ # Make sure the file exists, else create
+ #Note we use open_plus and and create file, so if Bastille is
+ #reverted, all the flags will self-clear (file deleted)
+ my $flagNumber = 0;
+ my $flagData = '';
+ foreach my $key (keys %GLOBAL_CONFIG) {
+ if ($GLOBAL_CONFIG{$key}{"TODOFlag"} eq "yes") {
+ ++$flagNumber;
+ $flagData .= "$key\n";
+ }
+ }
+ if (not( -e $todo_flag)) {
+ &B_log("DEBUG","Initializing TODO Flag file: $todo_flag");
+ &B_create_file($todo_flag); # Make sure it exists
+ }
+ &B_blank_file($todo_flag,
+ "This will not appear in the file; ensures blanking");
+ return &B_append_line($todo_flag, "", "$flagData"); #return success of save
+ } elsif (($action eq "isSet") and ($module ne "")) {
+ if ($GLOBAL_CONFIG{"$module"}{"TODOFlag"} eq "yes") {
+ return 1; #TRUE
+ } else {
+ return 0; #FALSE
+ }
+ } elsif (($action eq "set") and ($module ne "")) {
+ $GLOBAL_CONFIG{"$module"}{"TODOFlag"} = "yes";
+ } elsif (($action eq "clear") and ($module ne "")) {
+ $GLOBAL_CONFIG{"$module"}{"TODOFlag"} = "";
+ } else {
+ &B_log("ERROR","TODO_Flag Called with invalid parameters: $action , $module".
+ "audit report may be incorrect.");
+ return 0; #FALSE
+ }
+}
+
+1;
+
+
diff --git a/recipes-security/bastille/files/HPSpecific.pm b/recipes-security/bastille/files/HPSpecific.pm
new file mode 100644
index 0000000..7e7d709
--- /dev/null
+++ b/recipes-security/bastille/files/HPSpecific.pm
@@ -0,0 +1,1983 @@
+package Bastille::API::HPSpecific;
+
+use strict;
+use Bastille::API;
+use Bastille::API::FileContent;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(
+getIPFLocation
+getGlobalSwlist
+B_check_system
+B_swmodify
+B_load_ipf_rules
+B_Schedule
+B_ch_rc
+B_set_value
+B_chperm
+B_install_jail
+B_list_processes
+B_list_full_processes
+B_deactivate_inetd_service
+B_get_rc
+B_set_rc
+B_chrootHPapache
+isSystemTrusted
+isTrustedMigrationAvailable
+checkServiceOnHPUX
+B_get_path
+convertToTrusted
+isOKtoConvert
+convertToShadow
+getSupportedSettings
+B_get_sec_value
+secureIfNoNameService
+isUsingRemoteNameService
+remoteServiceCheck
+remoteNISPlusServiceCheck
+B_create_nsswitch_file
+B_combine_service_results
+
+%priorBastilleNDD
+%newNDD
+);
+our @EXPORT = @EXPORT_OK;
+
+
+
+# "Constants" for use both in testing and in lock-down
+our %priorBastilleNDD = (
+ "ip_forward_directed_broadcasts" =>["ip", "0"],
+ "ip_forward_src_routed" =>["ip", "0"],
+ "ip_forwarding" =>["ip", "0"],
+ "ip_ire_gw_probe" =>["ip", "0"],
+ "ip_pmtu_strategy" =>["ip", "1"],
+ "ip_respond_to_echo_broadcast" =>["ip", "0"],
+ "ip_send_redirects" =>["ip", "0"],
+ "ip_send_source_quench" =>["ip", "0"],
+ "tcp_syn_rcvd_max" =>["tcp","1000"],
+ "tcp_conn_request_max" =>["tcp","4096"] );
+
+our %newNDD = (
+ "ip_forward_directed_broadcasts" =>["ip", "0"],
+ "ip_forward_src_routed" =>["ip", "0"],
+ "ip_forwarding" =>["ip", "0"],
+ "ip_ire_gw_probe" =>["ip", "0"],
+ "ip_pmtu_strategy" =>["ip", "1"],
+ "ip_respond_to_echo_broadcast" =>["ip", "0"],
+ "ip_send_redirects" =>["ip", "0"],
+ "ip_send_source_quench" =>["ip", "0"],
+ "tcp_syn_rcvd_max" =>["tcp","4096"],
+ "tcp_conn_request_max" =>["tcp","4096"],
+ "arp_cleanup_interval" =>["arp","60000"],
+ "ip_respond_to_timestamp" =>["ip", "0"],
+ "ip_respond_to_timestamp_broadcast" => ["ip","0"] );
+
+
+####################################################################
+#
+# This module makes up the HP-UX specific API routines.
+#
+####################################################################
+#
+# Subroutine Listing:
+# &HP_ConfigureForDistro: adds all used file names to global
+# hashes and generates a global IPD
+# hash for SD modification lookup.
+#
+# &getGlobalSwlist($): Takes a fully qualified file name
+# and returns product:filset info
+# for that file. returns undef if
+# the file is not present in the IPD
+#
+# &B_check_system: Runs a series of system queries to
+# determine if Bastille can be safely
+# ran on the current system.
+#
+# &B_swmodify($): Takes a file name and runs the
+# swmodify command on it so that the
+# IPD is updated after changes
+#
+# &B_System($$): Takes a system command and the system
+# command that should be used to revert
+# whatever was done. Returns 1 on
+# success and 0 on failure
+#
+# &B_Backtick($) Takes a command to run and returns its stdout
+# to be used in place of the prior prevelent use
+# of un-error-handled backticks
+#
+# &B_load_ipf_rules($): Loads a set of ipfrules into ipf, storing
+# current rules for later reversion.
+#
+# &B_Schedule($$): Takes a pattern and a crontab line.
+# Adds or replaces the crontab line to
+# the crontab file, depending on if a
+# line matches the pattern
+#
+# &B_ch_rc($$): Takes a the rc.config.d flag name and
+# new value as well as the init script
+# location. This will stop a services
+# and set the service so that it will
+# not be restarted.
+#
+# &B_set_value($$$): Takes a param, value, and a filename
+# and sets the given value in the file.
+# Uses ch_rc, but could be rewritten using
+# Bastille API calls to make it work on Linux
+#
+# &B_TODO($): Appends the give string to the TODO.txt
+# file.
+#
+# &B_chperm($$$$): Takes new perm owner and group of given
+# file. TO BE DEPRECATED!!!
+#
+# &B_install_jail($$): Takes the jail name and the jail config
+# script location for a give jail...
+# These scripts can be found in the main
+# directory e.g. jail.bind.hpux
+#
+#####################################################################
+
+##############################################################################
+#
+# HP-UX Bastille directory structure
+#
+##############################################################################
+#
+# /opt/sec_mgmt/bastille/bin/ -- location of Bastille binaries
+# /opt/sec_mgmt/bastille/lib/ -- location of Bastille modules
+# /opt/sec_mgmt/bastille/doc/ -- location of Bastille doc files
+#
+# /etc/opt/sec_mgmt/bastille/ -- location of Bastille config files
+#
+# /var/opt/sec_mgmt/bastille/log -- location of Bastille log files
+# /var/opt/sec_mgmt/bastille/revert -- directory holding all Bastille-
+# created revert scripts
+# /var/opt/sec_mgmt/bastille/revert/backup -- directory holding the original
+# files that Bastille modifies,
+# with permissions intact
+#
+##############################################################################
+
+sub getIPFLocation () { # Temporary until we get defined search space support
+ my $ipf=&getGlobal('BIN','ipf_new');
+ my $ipfstat=&getGlobal('BIN','ipfstat_new');
+ if (not(-e $ipf)) { # Detect if the binaries moved
+ $ipf = &getGlobal('BIN','ipf');
+ $ipfstat=&getGlobal('BIN','ipfstat');
+ }
+ return ($ipf, $ipfstat);
+}
+
+##############################################
+# Given a combination of service results, provided
+# in an array, this function combines the result into
+# a reasonable aggregate result
+##############################################
+
+sub B_combine_service_results(@){
+ my @results = @_;
+
+ #TODO: Consider greater sophistication wrt inconsistent, or not installed.
+
+ foreach my $result (@results) {
+ if (not(($result == SECURE_CAN_CHANGE) or
+ ($result == SECURE_CANT_CHANGE) or
+ ($result == NOT_INSTALLED()))) {
+ return NOTSECURE_CAN_CHANGE();
+ }
+ }
+ return SECURE_CANT_CHANGE();
+}
+
+####################################################################
+# &getGlobalSwlist ($file);
+# This function returns the product and fileset information for
+# a given file or directory if it exists in the IPD otherwise
+# it returns undefined "undef"
+#
+# uses $GLOBAL_SWLIST{"$FILE"}
+####################################################################
+sub getGlobalSwlist($){
+ no strict;
+ my $file = $_[0];
+
+
+ if(! %GLOBAL_SWLIST) {
+ # Generating swlist database for swmodify changes that will be required
+ # The database will be a hash of fully qualified file names that reference
+ # the files product name and fileset. These values are required to use
+ # swmodify...
+
+ # Files tagged 'is_volatile' in the IPD are not entered in the swlist database
+ # in order to avoid invoking swmodify if the file is changed later. Attempting to
+ # swmodify 'volatile' files is both unneccessary and complicated since swverify will
+ # not evaluate volatile files anyway, and adding another value to the swlist database
+ # would require complex code changes.
+
+ # temp variable to keep swlist command /usr/sbin/swlist
+ my $swlist = &getGlobal('BIN',"swlist");
+
+ # listing of each directory and file that was installed by SD on the target machine
+ my @fileList = `$swlist -a is_volatile -l file`;
+
+ # listing of each patch and the patches that supersede each.
+ # hash which is indexed by patch.fileset on the system
+ my %patchSuperseded;
+
+ my @patchList = `${swlist} -l fileset -a superseded_by *.*,c=patch 2>&1`;
+ # check to see if any patches are present on the system
+ if(($? >> 8) == 0) {
+
+ # determining patch suppression for swmodify.
+ foreach my $patchState (@patchList) {
+ # removing empty lines and commented lines.
+ if($patchState !~ /^\s*\#/ && $patchState !~ /^\s*$/) {
+
+ # removing leading white space
+ $patchState =~ s/^\s+//;
+ my @patches = split /\s+/, $patchState;
+ if($#patches == 0){
+ # patch is not superseded
+ $patchSuperseded{$patches[0]} = 0;
+ }
+ else {
+ # patch is superseded
+ $patchSuperseded{$patches[0]} = 1;
+ }
+ }
+ }
+ }
+ else {
+ &B_log("DEBUG","No patches found on the system.\n");
+ }
+
+ if($#fileList >= 0){
+ # foreach line of swlist output
+ foreach my $fileEntry ( @fileList ){
+ #filter out commented portions
+ if( $fileEntry !~ /^\s*\#/ ){
+ chomp $fileEntry;
+ # split the output into three fields: product.fileset, filename, flag_isvolatile
+ my( $productInfo, $file, $is_volatile ) = $fileEntry =~ /^\s*(\S+): (\S+)\t(\S+)/ ;
+ # do not register volatile files
+ next if ($is_volatile =~ /true/); # skip to next file entry
+ $productInfo =~ s/\s+//;
+ $file =~ s/\s+//;
+ # if the product is a patch
+ if($productInfo =~ /PH(CO|KL|NE|SS)/){
+ # if the patch is not superseded by another patch
+ if($patchSuperseded{$productInfo} == 0){
+ # add the patch to the list of owner for this file
+ push @{$GLOBAL_SWLIST{"$file"}}, $productInfo;
+ }
+ }
+ # not a patch.
+ else {
+ # add the product to the list of owners for this file
+ push @{$GLOBAL_SWLIST{"$file"}}, $productInfo;
+ }
+
+ }
+ }
+ }
+ else{
+ # defining GLOBAL_SWLIST in error state.
+ $GLOBAL_SWLIST{"ERROR"} = "ERROR";
+ &B_log("ERROR","Could not execute swlist. Swmodifys will not be attempted");
+ }
+ }
+
+ if(exists $GLOBAL_SWLIST{"$file"}){
+ return $GLOBAL_SWLIST{"$file"};
+ }
+ else {
+ return undef;
+ }
+}
+
+###################################################################
+# &B_check_system;
+# This subroutine is called to validate that bastille may be
+# safely run on the current system. It will check to insure
+# that there is enough file system space, mounts are rw, nfs
+# mounts are not mounted noroot, and swinstall, swremove and
+# swmodify are not running
+#
+# uses ErrorLog
+#
+##################################################################
+sub B_check_system {
+ # exitFlag is one if a conflict with the successful execution
+ # of bastille is found.
+ my $exitFlag = 0;
+
+ my $ignoreCheck = &getGlobal("BDIR","config") . "/.no_system_check";
+ if( -e $ignoreCheck ) {
+ return $exitFlag;
+ }
+
+ # first check for swinstall, swmodify, or swremove processes
+ my $ps = &getGlobal('BIN',"ps") . " -el";
+ my @processTable = `$ps`;
+ foreach my $process (@processTable) {
+ if($process =~ /swinstall/ ) {
+ &B_log("ERROR","Bastille cannot run while a swinstall is in progress.\n" .
+ "Complete the swinstall operation and then run Bastille.\n\n");
+ $exitFlag = 1;
+ }
+
+ if($process =~ /swremove/ ) {
+ &B_log("ERROR","Bastille cannot run while a swremove is in progress.\n" .
+ "Complete the swremove operation and then run Bastille.\n\n");
+ $exitFlag = 1;
+ }
+
+ if($process =~ /swmodify/ ) {
+ &B_log("ERROR","Bastille cannot run while a swmodify is in progress.\n" .
+ "Complete the swmodify operation and then run Bastille.\n\n");
+ $exitFlag = 1;
+ }
+
+ }
+
+ # check for root read only mounts for /var /etc /stand /
+ # Bastille is required to make changes to these file systems.
+ my $mount = &getGlobal('BIN',"mount");
+ my $rm = &getGlobal('BIN',"rm");
+ my $touch = &getGlobal('BIN',"touch");
+
+ my @mnttab = `$mount`;
+
+ if(($? >> 8) != 0) {
+ &B_log("WARNING","Unable to use $mount to determine if needed partitions\n" .
+ "are root writable, based on disk mount options.\n" .
+ "Bastille will continue but note that disk\n" .
+ "mount checks were skipped.\n\n");
+ }
+ else {
+ foreach my $record (@mnttab) {
+ my @fields = split /\s+/, $record;
+ if ((defined $fields[0]) && (defined $fields[2]) && (defined $fields[3])) {
+ my $mountPoint = $fields[0];
+ my $mountType = $fields[2];
+ my $mountOptions = $fields[3];
+
+ # checks for /stand and /var/* removed
+ if($mountPoint =~ /^\/$|^\/etc|^\/var$/) {
+
+ if($mountOptions =~ /^ro,|,ro,|,ro$/) {
+ &B_log("ERROR","$mountPoint is mounted read-only. Bastille needs to make\n" .
+ "modifications to this file system. Please remount\n" .
+ "$mountPoint read-write and then run Bastille again.\n\n");
+ $exitFlag = 1;
+ }
+ # looking for an nfs mounted file system
+ if($mountType =~/.+:\//){
+ my $fileExisted=0;
+ if(-e "$mountPoint/.bastille") {
+ $fileExisted=1;
+ }
+
+ `$touch $mountPoint/.bastille 1>/dev/null 2>&1`;
+
+ if( (! -e "$mountPoint/.bastille") || (($? >> 8) != 0) ) {
+ &B_log("ERROR","$mountPoint is an nfs mounted file system that does\n" .
+ "not allow root to write to. Bastille needs to make\n" .
+ "modifications to this file system. Please remount\n" .
+ "$mountPoint giving root access and then run Bastille\n" .
+ "again.\n\n");
+
+ $exitFlag = 1;
+ }
+ # if the file did not exist befor the touch then remove the generated file
+ if(! $fileExisted) {
+ `$rm -f $mountPoint/.bastille 1>/dev/null 2>&1`;
+ }
+ }
+ }
+ }
+ else {
+ &B_log("WARNING","Unable to use $mount to determine if needed partitions\n" .
+ "are root writable, based on disk mount options.\n" .
+ "Bastille will continue but note that disk\n" .
+ "mount checks were skipped.\n\n");
+ }
+ }
+
+ }
+
+ # checks for enough disk space in directories that Bastille writes to.
+ my $bdf = &getGlobal('BIN',"bdf");
+ #directories that Bastille writes to => required space in kilobytes.
+ my %bastilleDirs = ( "/etc/opt/sec_mgmt/bastille" => "4", "/var/opt/sec_mgmt/bastille"=> "1000");
+ for my $directory (sort keys %bastilleDirs) {
+ my @diskUsage = `$bdf $directory`;
+
+ if(($? >> 8) != 0) {
+ &B_log("WARNING","Unable to use $bdf to determine disk usage for\n" .
+ "$directory\n" .
+ "Bastille will continue but note that disk\n" .
+ "usage checks were skipped.\n\n");
+
+ }
+ else {
+ # removing bdf header line from usage information.
+ shift @diskUsage;
+ my $usageString= "";
+
+ foreach my $usageRecord (@diskUsage) {
+ chomp $usageRecord;
+ $usageString .= $usageRecord;
+ }
+
+ $usageString =~ s/^\s+//;
+
+ my @fields = split /\s+/, $usageString;
+ if($#fields != 5) {
+ &B_log("WARNING","Unable to use $bdf to determine disk usage for\n" .
+ "$directory\n" .
+ "Bastille will continue but note that disk\n" .
+ "usage checks were skipped.\n\n");
+ }
+ else {
+
+ my $mountPoint = $fields[5];
+ my $diskAvail = $fields[3];
+
+ if($diskAvail <= $bastilleDirs{"$directory"}) {
+ &B_log("ERROR","$mountPoint does not contain enough available space\n" .
+ "for Bastille to run properly. $directory needs\n" .
+ "at least $bastilleDirs{$directory} kilobytes of space.\n" .
+ "Please clear at least that amount of space from\n" .
+ "$mountPoint and run Bastille again.\n" .
+ "Current Free Space available = ${diskAvail} k\n\n");
+ $exitFlag = 1;
+ }
+ }
+ }
+ }
+
+ # check to make sure that we are in at least run level 2 before we attempt to run
+ my $who = &getGlobal('BIN', "who") . " -r";
+ my $levelInfo = `$who`;
+ if(($? >> 8) != 0 ) {
+ &B_log("WARNING","Unable to use \"$who\" to determine system run.\n" .
+ "level Bastille will continue but note that the run\n" .
+ "level check was skipped.\n\n");
+ }
+ else {
+ chomp $levelInfo;
+ my @runlevel = split /\s+/, $levelInfo;
+ if ((! defined $runlevel[3]) or ($runlevel[3] < 2)) {
+ &B_log("WARNING","Bastille requires a run-level of 2 or more to run properly.\n" .
+ "Please move your system to a higher run level and then\n" .
+ "run 'bastille -b'.\n\n");
+ if(defined $runlevel[3]) {
+ &B_log("ERROR","Current run-level is '$runlevel[3]'.\n\n");
+ $exitFlag=1;
+ }
+ else {
+ &B_log("WARNING","Unable to use \"$who\" to determine system run.\n" .
+ "level Bastille will continue but note that the run\n" .
+ "level check was skipped.\n\n");
+ }
+ }
+ else {
+ &B_log("DEBUG","System run-level is $runlevel[3]\n");
+ }
+ }
+
+ if($exitFlag) {
+ exit(1);
+ }
+
+}
+
+###################################################################
+# &B_swmodify($file);
+# This subroutine is called after a file is modified. It will
+# redefine the file in the IPD with it's new properties. If
+# the file is not in the IPD it does nothing.
+#
+# uses B_System to make the swmodifications.
+##################################################################
+sub B_swmodify($){
+ my $file = $_[0];
+ if(defined &getGlobalSwlist($file)){
+ my $swmodify = &getGlobal('BIN',"swmodify");
+ my @productsInfo = @{&getGlobalSwlist($file)};
+ # running swmodify on files that were altered by this function but
+ # were created and maintained by SD
+ foreach my $productInfo (@productsInfo) {
+ &B_System("$swmodify -x files='$file' $productInfo",
+ "$swmodify -x files='$file' $productInfo");
+ }
+ }
+}
+
+####################################################################
+# &B_load_ipf_rules($ipfruleset);
+# This function enables an ipfruleset. It's a little more
+# specific than most API functions, but necessary because
+# ipf doesn't return correct exit codes (syntax error results
+# in a 0 exit code)
+#
+# uses ActionLog and ErrorLog to log
+# calls crontab directly (to list and to read in new jobs)
+###################################################################
+sub B_load_ipf_rules ($) {
+ my $ipfruleset=$_[0];
+
+ &B_log("DEBUG","# sub B_load_ipf_rules");
+
+ # TODO: grab ipf.conf dynamically from the rc.config.d files
+ my $ipfconf = &getGlobal('FILE','ipf.conf');
+
+ # file system changes - these are straightforward, and the API
+ # will take care of the revert
+ &B_create_file($ipfconf);
+ &B_blank_file($ipfconf, 'a$b');
+ &B_append_line($ipfconf, 'a$b', $ipfruleset);
+
+ # runtime changes
+
+ # define binaries
+ my $grep = &getGlobal('BIN', 'grep');
+ my ($ipf, $ipfstat) = &getIPFLocation;
+ # create backup rules
+ # This will exit with a non-zero exit code because of the grep
+ my @oldrules = `$ipfstat -io 2>&1 | $grep -v empty`;
+
+ my @errors=`$ipf -I -Fa -f $ipfconf 2>&1`;
+
+ if(($? >> 8) == 0) {
+
+ &B_set_rc("IPF_START","1");
+ &B_set_rc("IPF_CONF","$ipfconf");
+
+ # swap the rules in
+ &B_System("$ipf -s","$ipf -s");
+
+ # now create a "here" document with the previous version of
+ # the rules and put it into the revert-actions script
+ &B_revert_log("$ipf -I -Fa -f - <<EOF\n@{oldrules}EOF");
+
+ if (@errors) {
+ &B_log("ERROR","ipfilter produced the following errors when\n" .
+ " loading $ipfconf. You probably had an invalid\n" .
+ " rule in ". &getGlobal('FILE','customipfrules') ."\n".
+ "@errors\n");
+ }
+
+ } else {
+ &B_log("ERROR","Unable to run $ipf\n");
+ }
+
+}
+
+
+
+####################################################################
+# &B_Schedule($pattern,$cronjob);
+# This function schedules a cronjob. If $pattern exists in the
+# crontab file, that job will be replaced. Otherwise, the job
+# will be appended.
+#
+# uses ActionLog and ErrorLog to log
+# calls crontab directly (to list and to read in new jobs)
+###################################################################
+sub B_Schedule ($$) {
+ my ($pattern,$cronjob)=@_;
+ $cronjob .= "\n";
+
+ &B_log("DEBUG","# sub B_Schedule");
+ my $crontab = &getGlobal('BIN','crontab');
+
+ my @oldjobs = `$crontab -l 2>/dev/null`;
+ my @newjobs;
+ my $patternfound=0;
+
+ foreach my $oldjob (@oldjobs) {
+ if (($oldjob =~ m/$pattern/ ) and (not($patternfound))) {
+ push @newjobs, $cronjob;
+ $patternfound=1;
+ &B_log("ACTION","changing existing cron job which matches $pattern with\n" .
+ "$cronjob");
+ } elsif ($oldjob !~ m/$pattern/ ) {
+ &B_log("ACTION","keeping existing cron job $oldjob");
+ push @newjobs, $oldjob;
+ } #implied: else if pattern matches, but we've
+ #already replaced one, then toss the others.
+ }
+
+ unless ($patternfound) {
+ &B_log("ACTION","adding cron job\n$cronjob\n");
+ push @newjobs, $cronjob;
+ }
+
+ if(open(CRONTAB, "|$crontab - 2> /dev/null")) {
+ print CRONTAB @newjobs;
+
+ # now create a "here" document with the previous version of
+ # the crontab file and put it into the revert-actions script
+ &B_revert_log("$crontab <<EOF\n" . "@oldjobs" . "EOF");
+ close CRONTAB;
+ }
+
+ # Now check to make sure it happened, since cron will exit happily
+ # (retval 0) with no changes if there are any syntax errors
+ my @editedjobs = `$crontab -l 2>/dev/null`;
+
+ if (@editedjobs ne @newjobs) {
+ &B_log("ERROR","failed to add cron job:\n$cronjob\n" .
+ " You probably had an invalid crontab file to start with.");
+ }
+
+}
+
+
+#This function turns off a service, given a service name defined in HP-UX.service
+
+sub B_ch_rc($) {
+
+ my ($service_name)=@_;
+
+ if (&GetDistro != "^HP-UX") {
+ &B_log("ERROR","Tried to call ch_rc $service_name on a non-HP-UX\n".
+ " system! Internal Bastille error.");
+ return undef;
+ }
+ my $configfile="";
+ my $command = &getGlobal('BIN', 'ch_rc');
+
+ my $startup_script=&getGlobal('DIR','initd') . "/". $service_name;
+ my @rc_parameters= @{ &getGlobal('SERVICE',$service_name) };
+ my @rcFiles=@{ &getGlobal('RCCONFIG',$service_name) };
+ my $rcFile='';
+ if (@rcFiles == 1){
+ $rcFile=$rcFiles[0];
+ } else {
+ &B_log("FATAL","Multiple RC Files not yet supported... internal error.");
+ }
+
+ # if the service-related process is not run, and the control variable is stilll 1
+ # there is a inconsistency. in this case we only need to change the control variable
+ my @psnames=@{ &getGlobal('PROCESS',$service_name)};
+ my @processes;
+ foreach my $psname (@psnames) {
+ $psname .= '\b'; # avoid embedded match; anchor search pattern to trailing word boundry
+ my @procList = &isProcessRunning($psname);
+ if(@procList >= 0){
+ splice @processes,$#processes+1,0,@procList;
+ }
+ }
+#Actually set the rc variable
+ foreach my $rcVariable (@rc_parameters){
+ my $orig_value = &B_get_rc($rcVariable);
+ if ($orig_value eq "" ) { #If variable not set, used the defined file
+ $configfile=&getGlobal("DIR","rc.config.d") . "/" . $rcFile;
+ if (not( -f $configfile )) {
+ &B_create_file($configfile);
+ }
+ }
+ &B_log("DEBUG","In B_ch_rc (no procs), setting $rcVariable to 0 in $configfile" .
+ ", with an original value of $orig_value with rcfile: $rcFile");
+ if ( ! @processes) { # IF there are no processes we don't neet to perform a "stop"
+ &B_set_rc($rcVariable, "0", $configfile);
+ } else {
+ if ( $orig_value !~ "1" ) { #If param is not already 1, the "stop" script won't work
+ &B_set_rc($rcVariable, "1",$configfile);
+ }
+ &B_System ($startup_script . " stop", #stop service, then restart if the user runs bastille -r
+ $startup_script . " start");
+ # set parameter, so that service will stay off after reboots
+ &B_set_rc($rcVariable, "0", $configfile);
+ }
+ }
+}
+
+
+# This routine sets a value in a given file
+sub B_set_value($$$) {
+ my ($param, $value, $file)=@_;
+
+ &B_log("DEBUG","B_set_value: $param, $value, $file");
+ if (! -e $file ) {
+ &B_create_file("$file");
+ }
+
+ # If a value is already set to something other than $value then reset it.
+ #Note that though this tests for "$value ="the whole line gets replaced, so
+ #any pre-existing values are also replaced.
+ &B_replace_line($file,"^$param\\s*=\\s*","$param=$value\n");
+ # If the value is not already set to something then set it.
+ &B_append_line($file,"^$param\\s*=\\s*$value","$param=$value\n");
+
+}
+
+
+##################################################################################
+# &B_chperm($owner,$group,$mode,$filename(s))
+# This function changes ownership and mode of a list of files. Takes four
+# arguments first the owner next the group and third the new mode in oct and
+# last a list of files that the permissions changes should take affect on.
+#
+# uses: &swmodify and &B_revert_log
+##################################################################################
+sub B_chperm($$$$) {
+ my ($newown, $newgrp, $newmode, $file_expr) = @_;
+ my @files = glob($file_expr);
+
+ my $return = 1;
+
+ foreach my $file (@files){
+ my @filestat = stat $file;
+ my $oldmode = (($filestat[2]/512) % 8) .
+ (($filestat[2]/64) % 8) .
+ (($filestat[2]/8) % 8) .
+ (($filestat[2]) % 8);
+
+ if((chown $newown, $newgrp, $file) != 1 ){
+ &B_log("ERROR","Could not change ownership of $file to $newown:$newgrp\n");
+ $return = 0;
+ }
+ else{
+ &B_log("ACTION","Changed ownership of $file to $newown:$newgrp\n");
+ # swmodifying file if possible...
+ &B_swmodify($file);
+ &B_revert_log(&getGlobal('BIN',"chown") . " $filestat[4]:$filestat[5] $file\n");
+ }
+
+ my $newmode_formatted=sprintf "%5lo",$newmode;
+
+ if((chmod $newmode, $file) != 1){
+ &B_log("ERROR","Could not change mode of $file to $newmode_formatted\n");
+ $return = 0;
+ }
+ else{
+ &B_log("ACTION","Changed mode of $file to $newmode_formatted\n");
+ &B_revert_log(&getGlobal('BIN',"chmod") . " $oldmode $file\n");
+ }
+
+
+ }
+ return $return;
+}
+
+############################################################################
+# &B_install_jail($jailname, $jailconfigfile);
+# This function takes two arguments ( jail_name, jail_config )
+# It's purpose is to take read in config files that define a
+# chroot jail and then generate it bases on that specification
+############################################################################
+sub B_install_jail($$) {
+
+ my $jailName = $_[0]; # Name of the jail e.g bind
+ my $jailConfig = $_[1]; # Name of the jails configuration file
+ # create the root directory of the jail if it does not exist
+ &B_create_dir( &getGlobal('BDIR','jail'));
+ &B_chperm(0,0,0555,&getGlobal('BDIR','jail'));
+
+ # create the Jail dir if it does not exist
+ &B_create_dir( &getGlobal('BDIR','jail') . "/" . $jailName);
+ &B_chperm(0,0,0555,&getGlobal('BDIR','jail') . "/". $jailName);
+
+
+ my $jailPath = &getGlobal('BDIR','jail') . "/" . $jailName;
+ my @lines; # used to store no commented no empty config file lines
+ # open configuration file for desired jail and parse in commands
+ if(open(JAILCONFIG,"< $jailConfig")) {
+ while(my $line=<JAILCONFIG>){
+ if($line !~ /^\s*\#|^\s*$/){
+ chomp $line;
+ push(@lines,$line);
+ }
+ }
+ close JAILCONFIG;
+ }
+ else{
+ &B_log("ERROR","Open Failed on filename: $jailConfig\n");
+ return 0;
+ }
+ # read through commands and execute
+ foreach my $line (@lines){
+ &B_log("ACTION","Install jail: $line\n");
+ my @confCmd = split /\s+/,$line;
+ if($confCmd[0] =~ /dir/){ # if the command say to add a directory
+ if($#confCmd == 4) { # checking dir Cmd form
+ if(! (-d $jailPath . "/" . $confCmd[1])){
+ #add a directory and change its permissions according
+ #to the conf file
+ &B_create_dir( $jailPath . "/" . $confCmd[1]);
+ &B_chperm((getpwnam($confCmd[3]))[2],
+ (getgrnam($confCmd[4]))[2],
+ oct($confCmd[2]),
+ $jailPath . "/" . $confCmd[1]);
+ }
+ }
+ else {
+ &B_log("ERROR","Badly Formed Configuration Line:\n$line\n\n");
+ }
+ }
+ elsif($confCmd[0] =~ /file/) {
+ if($#confCmd == 5) { # checking file cmd form
+ if(&B_cp($confCmd[1],$jailPath . "/" . $confCmd[2])){
+ # for copy command cp file and change perms
+ &B_chperm($confCmd[4],$confCmd[5],oct($confCmd[3]),$jailPath . "/" . $confCmd[2]);
+ }
+ else {
+ &B_log("ERROR","Could not complete copy on specified files:\n" .
+ "$line\n");
+ }
+ }
+ else {
+ &B_log("ERROR","Badly Formed Configuration Line:\n" .
+ "$line\n\n");
+ }
+ }
+ elsif($confCmd[0] =~ /slink/) {
+ if($#confCmd == 2) { # checking file cmd form
+ if(!(-e $jailPath . "/" . $confCmd[2])){
+ #for symlink command create the symlink
+ &B_symlink($jailPath . "/" . $confCmd[1], $confCmd[2]);
+ }
+ }
+ else {
+ &B_log("ERROR","Badly Formed Configuration Line:\n" .
+ "$line\n\n");
+ }
+ }
+ else {
+ &B_log("ERROR","Unrecognized Configuration Line:\n" .
+ "$line\n\n");
+ }
+ }
+ return 1;
+}
+
+
+
+###########################################################################
+# &B_list_processes($service) #
+# #
+# This subroutine uses the GLOBAL_PROCESS hash to determine if a #
+# service's corresponding processes are running on the system. #
+# If any of the processes are found to be running then the process #
+# name(s) is/are returned by this subroutine in the form of an list #
+# If none of the processes that correspond to the service are running #
+# then an empty list is returned. #
+###########################################################################
+sub B_list_processes($) {
+
+ # service name
+ my $service = $_[0];
+ # list of processes related to the service
+ my @processes=@{ &getGlobal('PROCESS',$service)};
+
+ # current systems process information
+ my $ps = &getGlobal('BIN',"ps");
+ my $psTable = `$ps -elf`;
+
+ # the list to be returned from the function
+ my @running_processes;
+
+ # for every process associated with the service
+ foreach my $process (@processes) {
+ # if the process is in the process table then
+ if($psTable =~ m/$process/) {
+ # add the process to the list, which will be returned
+ push @running_processes, $process;
+ }
+
+ }
+
+ # return the list of running processes
+ return @running_processes;
+
+}
+
+#############################################################################
+# &B_list_full_processes($service) #
+# #
+# This subroutine simply grep through the process table for those matching #
+# the input argument TODO: Allow B_list process to levereage this code #
+# ... Not done this cycle to avoid release risk (late in cycle) #
+#############################################################################
+sub B_list_full_processes($) {
+
+ # service name
+ my $procName = $_[0];
+ my $ps = &getGlobal('BIN',"ps");
+ my @psTable = split(/\n/,`$ps -elf`);
+
+ # for every process associated with the service
+ my @runningProcessLines = grep(/$procName/ , @psTable);
+ # return the list of running processes
+ return @runningProcessLines;
+}
+
+################################################################################
+# &B_deactivate_inetd_service($service); #
+# #
+# This subroutine will disable all inetd services associated with the input #
+# service name. Service name must be a reference to the following hashes #
+# GLOBAL_SERVICE GLOBAL_SERVTYPE and GLOBAL_PROCESSES. If processes are left #
+# running it will note these services in the TODO list as well as instruct the#
+# user in how they remaining processes can be disabled. #
+################################################################################
+sub B_deactivate_inetd_service($) {
+ my $service = $_[0];
+ my $servtype = &getGlobal('SERVTYPE',"$service");
+ my $inetd_conf = &getGlobal('FILE',"inetd.conf");
+
+ # check the service type to ensure that it can be configured by this subroutine.
+ if($servtype ne 'inet') {
+ &B_log("ACTION","The service \"$service\" is not an inet service so it cannot be\n" .
+ "configured by this subroutine\n");
+ return 0;
+ }
+
+ # check for the inetd configuration files existence so it may be configured by
+ # this subroutine.
+ if(! -e $inetd_conf ) {
+ &B_log("ACTION","The file \"$inetd_conf\" cannot be located.\n" .
+ "Unable to configure inetd\n");
+ return 0;
+ }
+
+ # list of service identifiers present in inetd.conf file.
+ my @inetd_entries = @{ &getGlobal('SERVICE',"$service") };
+
+ foreach my $inetd_entry (@inetd_entries) {
+ &B_hash_comment_line($inetd_conf, "^\\s*$inetd_entry");
+ }
+
+ # list of processes associated with this service which are still running
+ # on the system
+ my @running_processes = &B_list_processes($service);
+
+ if($#running_processes >= 0) {
+ my $todoString = "\n" .
+ "---------------------------------------\n" .
+ "Deactivating Inetd Service: $service\n" .
+ "---------------------------------------\n" .
+ "The following process(es) are associated with the inetd service \"$service\".\n" .
+ "They are most likely associated with a session which was initiated prior to\n" .
+ "running Bastille. To disable a process see \"kill(1)\" man pages or reboot\n" .
+ "the system\n" .
+ "Active Processes:\n" .
+ "###################################\n";
+ foreach my $running_process (@running_processes) {
+ $todoString .= "\t$running_process\n";
+ }
+ $todoString .= "###################################\n";
+
+ &B_TODO($todoString);
+ }
+
+}
+
+
+################################################################################
+# B_get_rc($key); #
+# #
+# This subroutine will use the ch_rc binary to get rc.config.d variables #
+# values properly escaped and quoted. #
+################################################################################
+sub B_get_rc($) {
+
+ my $key=$_[0];
+ my $ch_rc = &getGlobal('BIN',"ch_rc");
+
+ # get the current value of the given parameter.
+ my $currentValue=`$ch_rc -l -p $key`;
+ chomp $currentValue;
+
+ if(($? >> 8) == 0 ) {
+ # escape all meta characters.
+ # $currentValue =~ s/([\"\`\$\\])/\\$1/g;
+ # $currentValue = '"' . $currentValue . '"';
+ }
+ else {
+ return undef;
+ }
+
+ return $currentValue;
+}
+
+
+
+################################################################################
+# B_set_rc($key,$value); #
+# #
+# This subroutine will use the ch_rc binary to set rc.config.d variables. As #
+# well as setting the variable this subroutine will set revert strings. #
+# #
+################################################################################
+sub B_set_rc($$;$) {
+
+ my ($key,$value,$configfile)=@_;
+ my $ch_rc = &getGlobal('BIN',"ch_rc");
+
+ # get the current value of the given parameter.
+ my $currentValue=&B_get_rc($key);
+ if(defined $currentValue ) {
+ if ($currentValue =~ /^\"(.*)\"$/ ) {
+ $currentValue = '"\"' . $1 . '\""';
+ }
+ if ($value =~ /^\"(.*)\"$/ ) {
+ $value = '"\"' . $1 . '\""';
+ }
+ if ( &B_System("$ch_rc -a -p $key=$value $configfile",
+ "$ch_rc -a -p $key=$currentValue $configfile") ) {
+ #ch_rc success
+ return 1;
+ }
+ else {
+ #ch_rc failure.
+ return 0;
+ }
+ }
+ else {
+ &B_log("ERROR","ch_rc was unable to lookup $key\n");
+ return 0;
+ }
+
+}
+
+
+################################################################################
+# &ChrootHPApache($chrootScript,$httpd_conf,$httpd_bin,
+# $apachectl,$apacheJailDir,$serverString);
+#
+# This subroutine given an chroot script, supplied by the vendor, a
+# httpd.conf file, the binary location of httpd, the control script,
+# the jail directory, and the servers identification string, descriptive
+# string for TODO etc. It makes modifications to httpd.conf so that when
+# Apache starts it will chroot itself into the jail that the above
+# mentions script creates.
+#
+# uses B_replace_line B_create_dir B_System B_TODO
+#
+###############################################################################
+sub B_chrootHPapache($$$$$$) {
+
+ my ($chrootScript,$httpd_conf,$httpd_bin,$apachectl,$apacheJailDir,$serverString)= @_;
+
+ my $exportpath = "export PATH=/usr/bin;";
+ my $ps = &getGlobal('BIN',"ps");
+ my $isRunning = 0;
+ my $todo_header = 0;
+
+ # checking for a 2.0 version of the apache chroot script.
+ if(-e $chrootScript ) {
+
+ if(open HTTPD, $httpd_conf) {
+ while (my $line = <HTTPD>){
+ if($line =~ /^\s*Chroot/) {
+ &B_log("DEBUG","Apache is already running in a chroot as specified by the following line:\n$line\n" .
+ "which appears in the httpd.conf file. No Apache Chroot action was taken.\n");
+ return;
+ }
+ }
+ close(HTTPD);
+ }
+
+ if(`$ps -ef` =~ $httpd_bin ) {
+ $isRunning=1;
+ &B_System("$exportpath " . $apachectl . " stop","$exportpath " . $apachectl . " start");
+ }
+ &B_replace_line($httpd_conf, '^\s*#\s*Chroot' ,
+ "Chroot " . $apacheJailDir);
+ if(-d &getGlobal('BDIR',"jail")){
+ &B_log("DEBUG","Jail directory already exists. No action taken.\n");
+ }
+ else{
+ &B_log("ACTION","Jail directory was created.\n");
+ &B_create_dir( &getGlobal('BDIR','jail'));
+ }
+
+ if(-d $apacheJailDir){
+ &B_log("DEBUG","$serverString jail already exists. No action taken.\n");
+ }
+ else{
+ &B_System(&getGlobal('BIN',"umask") . " 022; $exportpath " . $chrootScript,
+ &getGlobal('BIN',"echo") . " \"Your $serverString is now running outside of it's\\n" .
+ "chroot jail. You must manually migrate your web applications\\n" .
+ "back to your Apache server's httpd.conf defined location(s).\\n".
+ "After you have completed this, feel free to remove the jail directories\\n" .
+ "from your machine. Your apache jail directory is located in\\n" .
+ &getGlobal('BDIR',"jail") . "\\n\" >> " . &getGlobal('BFILE',"TOREVERT"));
+
+ }
+ if($isRunning){
+ &B_System("$exportpath " . $apachectl . " start","$exportpath " . $apachectl . " stop");
+ &B_log("ACTION","$serverString is now running in an chroot jail.\n");
+ }
+
+ &B_log("ACTION","The jail is located in " . $apacheJailDir . "\n");
+
+ if ($todo_header !=1){
+ &B_TODO("\n---------------------------------\nApache Chroot:\n" .
+ "---------------------------------\n");
+ }
+ &B_TODO("$serverString Chroot Jail:\n" .
+ "httpd.conf contains the Apache dependencies. You should\n" .
+ "review this file to ensure that the dependencies made it\n" .
+ "into the jail. Otherwise, you run a risk of your Apache server\n" .
+ "not having access to all its modules and functionality.\n");
+
+
+ }
+
+}
+
+
+sub isSystemTrusted {
+ my $getprdef = &getGlobal('BIN',"getprdef");
+ my $definition = &B_Backtick("$getprdef -t 2>&1");
+ if($definition =~ "System is not trusted.") {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+sub isTrustedMigrationAvailable {
+ my $distroVersion='';
+
+ if (&GetDistro =~ '^HP-UX11.(\d*)') {
+ $distroVersion=$1;
+ if ($distroVersion < 23) { # Not available before 11.23
+ return 0; #FALSE
+ } elsif ($distroVersion >= 31) { #Bundled with 11.31 and after
+ &B_log('DEBUG','isTrustedMigrationAvailable: HP-UX 11.31 always has trusted mode extensions');
+ return 1;
+ } elsif ($distroVersion == 23) { # Optional on 11.23 if filesets installed
+ if ( -x &getGlobal('BIN',"userdbget") ) {
+ &B_log('DEBUG','isTrustedMigrationAvailable: Trusted Extensions Installed');
+ return 1;
+ } else {
+ &B_log('DEBUG','isTrustedMigrationAvailable: Trusted Extensions Not Installed');
+ return 0; #FALSE
+ }
+ } else {
+ &B_log('DEBUG','isTrustedMigrationAvailable: ' . &GetDistro .
+ ' not currently supported for trusted extentions.');
+ return 0; #FALSE
+ }
+ } else {
+ &B_log('WARNING','isTrustedMigrationAvailable: HP-UX routine called on Linux system');
+ return 0; #FALSE
+ }
+}
+
+
+
+###########################################################################
+# &checkServiceOnHPUX($service);
+#
+# Checks if the given service is running on an HP/UX system. This is
+# called by B_is_Service_Off(), which is the function that Bastille
+# modules should call.
+#
+# Return values:
+# NOTSECURE_CAN_CHANGE() if the service is on
+# SECURE_CANT_CHANGE() if the service is off
+# INCONSISTENT() if the state of the service cannot be determined
+# NOT_INSTALLED() if the s/w isn't insalled
+#
+###########################################################################
+sub checkServiceOnHPUX($) {
+ my $service=$_[0];
+
+ # get the list of parameters which could be used to initiate the service
+ # (could be in /etc/rc.config.d, /etc/inetd.conf, or /etc/inittab, so we
+ # check all of them)
+ my @params= @{ &getGlobal('SERVICE',$service) };
+ my $grep =&getGlobal('BIN', 'grep');
+ my $inetd=&getGlobal('FILE', 'inetd.conf');
+ my $inittab=&getGlobal('FILE', 'inittab');
+ my $retVals;
+ my $startup=&getGlobal('DIR','initd') ;
+ my @inet_bins= @{ &getGlobal('PROCESS',$service) };
+
+ my $entry_found = 0;
+
+ &B_log("DEBUG","CheckHPUXservice: $service");
+ my $full_initd_path = $startup . "/" . $service;
+ if ($GLOBAL_SERVTYPE{$service} eq "rc") { # look for the init script in /sbin/init.d
+ if (not(-e $full_initd_path )) {
+ return NOT_INSTALLED();
+ }
+ } else { #inet-based service, so look for inetd.conf entries.
+ &B_log("DEBUG","Checking inet service $service");
+ my @inet_entries= @{ &getGlobal('SERVICE',$service) };
+ foreach my $service (@inet_entries) {
+ &B_log('DEBUG',"Checking for inetd.conf entry of $service in checkService on HPUX");
+ my $service_regex = '^[#\s]*' . $service . '\s+';
+ if ( &B_match_line($inetd, $service_regex) ) { # inet entry search
+ &B_log('DEBUG',"$service present, entry exists");
+ $entry_found = 1 ;
+ }
+ }
+ if ($entry_found == 0 ) {
+ return NOT_INSTALLED();
+ }
+ }
+
+ foreach my $param (@params) {
+ &B_log("DEBUG","Checking to see if service $service is off.\n");
+ if (&getGlobal('SERVTYPE', $service) =~ /rc/) {
+ my $ch_rc=&getGlobal('BIN', 'ch_rc');
+ my $on=&B_Backtick("$ch_rc -l -p $param");
+
+ $on =~ s/\s*\#.*$//; # remove end-of-line comments
+ $on =~ s/^\s*\"(.+)\"\s*$/$1/; # remove surrounding double quotes
+ $on =~ s/^\s*\'(.+)\'\s*$/$1/; # remove surrounding single quotes
+ $on =~ s/^\s*\"(.+)\"\s*$/$1/; # just in case someone did '"blah blah"'
+
+ chomp $on;
+ &B_log("DEBUG","ch_rc returned: $param=$on in checkServiceOnHPUX");
+
+ if ($on =~ /^\d+$/ && $on != 0) {
+ # service is on
+ &B_log("DEBUG","CheckService found $param service is set to \'on\' in scripts.");
+ return NOTSECURE_CAN_CHANGE();
+ }
+ elsif($on =~ /^\s*$/) {
+ # if the value returned is an empty string return
+ # INCONSISTENT(), since we don't know what the hard-coded default is.
+ return INCONSISTENT();
+ }
+ } else {
+ # those files which rely on comments to determine what gets
+ # turned on, such as inetd.conf and inittab
+ my $inettabs=&B_Backtick("$grep -e '^[[:space:]]*$param' $inetd $inittab");
+ if ($inettabs =~ /.+/) { # . matches anything except newlines
+ # service is not off
+ &B_log("DEBUG","Checking inetd.conf and inittab; found $inettabs");
+ ########################### BREAK out, don't skip question
+ return NOTSECURE_CAN_CHANGE();
+ }
+ }
+ } # foreach $param
+
+ # boot-time parameters are not set; check processes
+ # checkprocs for services returns INCONSISTENT() if a service is found
+ # since a found-service is inconsistent with the above checks.
+ B_log("DEBUG","Boot-Parameters not set, checking processes.");
+ if (&runlevel < 2) { # Below runlevel 2, it is unlikely that
+ #services will be running, so just check "on-disk" state
+ &B_log("NOTE","Running during boot sequence, so skipping process checks");
+ return SECURE_CANT_CHANGE();
+ } else {
+ return &checkProcsForService($service);
+ }
+}
+
+sub runlevel {
+ my $who = &getGlobal("BIN", "who");
+ my $runlevel = &B_Backtick("$who -r");
+ if ($runlevel =~ s/.* run-level (\S).*/$1/) {
+ &B_log("DEBUG","Runlevel is: $runlevel");
+ return $runlevel;
+ } else {
+ &B_log("WARNING","Can not determine runlevel, assuming runlevel 3");
+ &B_log("DEBUG","Runlevel command output: $runlevel");
+ return "3"; #safer since the who command didn't work, we'll assume
+ # runlevel 3 since that provides more checks.
+ }
+}
+
+#
+# given a profile file, it will return a PATH array set by the file.
+#
+sub B_get_path($) {
+ my $file = $_[0];
+ my $sh = &getGlobal("BIN", "sh");
+ # use (``)[0] is becuase, signal 0 maybe trapped which will produce some stdout
+ my $path = (`$sh -c '. $file 1>/dev/null 2>&1 < /dev/null ; echo \$PATH'`)[0];
+ my @path_arr = split(":", $path);
+ my %tmp_path;
+ my %path;
+ for my $tmpdir (@path_arr) {
+ chomp $tmpdir;
+ if ($tmpdir ne "" && ! $tmp_path{$tmpdir}) {
+ $tmp_path{$tmpdir}++;
+ }
+ }
+ return keys %tmp_path;
+}
+
+# Convert to trusted mode if it's not already
+sub convertToTrusted {
+ &B_log("DEBUG","# sub convertToTrusted \n");
+ if( ! &isSystemTrusted) {
+
+ my ($ok, $message) = &isOKtoConvert;
+
+ my $ts_header="\n---------------------------------\nTrusted Systems:\n" .
+ "---------------------------------\n";
+
+ if ($ok) {
+ # actually do the conversion
+ if(&B_System(&getGlobal('BIN','tsconvert'), &getGlobal('BIN','tsconvert') . " -r")){
+ # adjust change times for user passwords to keep them valid
+ # default is to expire them when converting to a trusted system,
+ # which can be problematic, especially since some older versions of
+ # SecureShell do not allow the user to change the password
+ &B_System(&getGlobal('BIN','modprpw') . " -V", "");
+
+ my $getprdef = &getGlobal('BIN','getprdef');
+ my $oldsettings = &B_Backtick("$getprdef -m lftm,exptm,mintm,expwarn,umaxlntr");
+ $oldsettings =~ s/ //g;
+
+ # remove password lifetime and increasing login tries so they
+ # don't lock themselves out of the system entirely.
+ # set default expiration time and the like.
+ my $newsettings="lftm=0,exptm=0,mintm=0,expwarn=0,umaxlntr=10";
+
+ &B_System(&getGlobal('BIN','modprdef') . " -m $newsettings",
+ &getGlobal('BIN','modprdef') . " -m $oldsettings");
+
+ &B_TODO($ts_header .
+ "Your system has been converted to a trusted system.\n" .
+ "You should review the security settings available on a trusted system.\n".
+ "$message");
+
+ # to get rid of "Cron: Your job did not contain a valid audit ID."
+ # error, we re-read the crontab file after converting to trusted mode
+ # Nothing is necessary in "revert" since we won't be in trusted mode
+ # at that time.
+ # crontab's errors can be spurious, and this will report an 'error'
+ # of the crontab file is missing, so we send stderr to the bit bucket
+ my $crontab = &getGlobal('BIN',"crontab");
+ &B_System("$crontab -l 2>/dev/null | $crontab","");
+ }
+
+ } else {
+ &B_TODO($ts_header . $message);
+ return 0; # not ok to convert, so we didn't
+ }
+ }
+ else {
+ &B_log("DEBUG","System is already in trusted mode, no action taken.\n");
+ return 1;
+ }
+
+ # just to make sure
+ if( &isSystemTrusted ) {
+ return 1;
+ } else {
+ &B_log("ERROR","Trusted system conversion was unsuccessful for an unknown reason.\n" .
+ " You may try using SAM/SMH to do the conversion instead of Bastille.\n");
+ return 0;
+ }
+}
+
+# isOKtoConvert - check for conflicts between current system state and trusted
+# mode
+#
+# Return values
+# 0 - conflict found, see message for details
+# 1 - no conflicts, see message for further instructions
+#
+sub isOKtoConvert {
+ &B_log("DEBUG","# sub isOKtoConvert \n");
+ # initialize text for TODO instructions
+ my $specialinstructions=" - convert to trusted mode\n";
+
+ # These are somewhat out-of-place, but only affect the text of the message.
+ # Each of these messages is repeated in a separate TODO item in the
+ # appropriate subroutine.
+ if (&getGlobalConfig("AccountSecurity","single_user_password") eq "Y") {
+ if (&GetDistro =~ "^HP-UX11.(.*)" and $1<23 ) {
+ $specialinstructions .= " - set a single user password\n";
+ }
+ }
+
+ if (&getGlobalConfig("AccountSecurity","passwordpolicies") eq "Y") {
+ $specialinstructions .= " - set trusted mode password policies\n";
+ }
+
+ if (&getGlobalConfig("AccountSecurity", "PASSWORD_HISTORY_DEPTHyn") eq "Y") {
+ $specialinstructions .= " - set a password history depth\n";
+ }
+
+ if (&getGlobalConfig("AccountSecurity","system_auditing") eq "Y") {
+ $specialinstructions .= " - enable auditing\n";
+ }
+
+ my $saminstructions=
+ "The security settings can be modified by running SAM as follows:\n" .
+ "# sam\n" .
+ "Next, go to the \"Auditing and Security Area\" and review\n" .
+ "each sub-section. Make sure that you review all of your\n" .
+ "settings, as some policies may seem restrictive.\n\n" .
+ "On systems using the System Management Homepage, you can\n".
+ "change your settings via the Tools:Security Attributes Configuration\n".
+ "section. On some systems, you may also have the option of using SMH.\n\n";
+
+ # First, check for possible conflicts and corner cases
+
+ # check nsswitch for possible conflicts
+ my $nsswitch = &getGlobal('FILE', 'nsswitch.conf');
+ if ( -e $nsswitch) {
+ open(FILE, $nsswitch);
+ while (<FILE>) {
+ if (/nis/ or /compat/ or /ldap/) {
+ my $message = "Bastille found a possible conflict between trusted mode and\n" .
+ "$nsswitch. Please remove all references to\n" .
+ "\"compat\", \"nis\" and \"ldap\" in $nsswitch\n" .
+ "and rerun Bastille, or use SAM/SMH to\n" .
+ "$specialinstructions\n".
+ "$saminstructions";
+ close(FILE);
+ return (0,$message);
+ }
+ }
+ close(FILE);
+ }
+
+ # check the namesvrs config file for possible NIS conflicts
+ #Changed to unless "Y AND Y" since question can be skipped when nis is off
+ # but corner cases can still exist, so check then too.
+ unless ( &getGlobalConfig('MiscellaneousDaemons','nis_client') eq "Y" and
+ &getGlobalConfig('MiscellaneousDaemons','nis_server') eq "Y" ) {
+ my $namesvrs = &getGlobal('FILE', 'namesvrs');
+ if (open(FILE, $namesvrs)) {
+ while (<FILE>) {
+ if (/^NIS.*=["]?1["]?$/) {
+ my $message= "Possible conflict between trusted mode and NIS found.\n".
+ "Please use SAM/SMH to\n" .
+ " - turn off NIS\n" .
+ "$specialinstructions\n".
+ "$saminstructions";
+ close(FILE);
+ return (0,$message);
+ }
+ }
+ close(FILE);
+ } else {
+ &B_log("ERROR","Unable to open $namesvrs for reading.");
+ my $message= "Possible conflict between trusted mode and NIS found.\n".
+ "Please use SAM/SMH to\n" .
+ " - turn off NIS\n" .
+ "$specialinstructions\n".
+ "$saminstructions";
+ return (0,$message);
+ }
+ if ( &B_match_line (&getGlobal("FILE","passwd"),"^\+:.*")) {
+ my $message= '"+" entry found in passwd file. These are not\n' .
+ "compatible with Trusted Mode. Either remove the entries\n" .
+ "and re-run Bastille, or re-run Bastille, and direct it to\n" .
+ "disable NIS client and server.\n";
+ return (0,$message);
+ }
+
+ }
+
+
+ # check for conflicts with DCE integrated login
+ my $authcmd = &getGlobal('BIN','auth.adm');
+ if ( -e $authcmd ) {
+ my $retval = system("PATH=/usr/bin $authcmd -q 1>/dev/null 2>&1");
+ if ($retval != 0 and $retval != 1) {
+ my $message="It appears that DCE integrated login is configured on this system.\n" .
+ "DCE integrated login is incompatible with trusted systems and\n" .
+ "auditing. Bastille is unable to\n" .
+ "$specialinstructions" .
+ "You will need to configure auditing and password policies using DCE.\n\n";
+ return (0,$message);
+ }
+ }
+
+ if ( -e &getGlobal('FILE','shadow') ) {
+ my $message="This system has already been converted to shadow passwords.\n" .
+ "Shadow passwords are incompatible with trusted mode.\n" .
+ "Bastille is unable to\n" .
+ "$specialinstructions" .
+ "If you desire these features, you should use\n".
+ "\'pwunconv\' to change back to standard passwords,\n".
+ "and then rerun Bastille.\n\n";
+ return (0,$message);
+ }
+
+ return (1,$saminstructions);
+}
+
+# This routine allows Bastille to determine trusted-mode extension availability
+
+sub convertToShadow {
+
+ if (&isSystemTrusted) {
+ # This is an internal error...Bastille should not call this routine
+ # in this case. Error is here for robustness against future changes.
+ &B_log("ERROR","This system is already converted to trusted mode.\n" .
+ " Converting to shadow passwords will not be attempted.\n");
+ return 0;
+ }
+
+ # configuration files on which shadowed passwords depend
+ my $nsswitch_conf = &getGlobal('FILE',"nsswitch.conf");
+
+ # binaries used to convert to a shadowed password
+ my $pwconv = &getGlobal('BIN',"pwconv");
+ my $echo = &getGlobal('BIN','echo'); # the echo is used to pipe a yes into the pwconv program as
+ # pwconv requires user interaction.
+
+ # the binary used in a system revert.
+ my $pwunconv = &getGlobal('BIN',"pwunconv");
+ #check the password file for nis usage and if the nis client
+ #or server is running.
+ if(-e $nsswitch_conf) {
+ # check the file for nis, nis+, compat, or dce usage.
+ if(&B_match_line($nsswitch_conf, '^\s*passwd:.+(nis|nisplus|dce|compat)')) {
+ my $shadowTODO = "\n---------------------------------\nHide encrypted passwords:\n" .
+ "---------------------------------\n" .
+ "This version of password shadowing does not support any repository other\n" .
+ "than files. In order to convert your password database to shadowed passwords\n" .
+ "there can be no mention of nis, nisplus, compat, or dce in the passwd\n" .
+ "field of the \"$nsswitch_conf\" file. Please make the necessary edits to\n" .
+ "the $nsswitch_conf file and run Bastille again using the command:\n" .
+ "\"bastille -b\"\n";
+ # Adding the shadowTODO comment to the TODO list.
+ &B_TODO("$shadowTODO");
+ # Notifing the user that the shadowed password coversion has failed.
+ &B_log("ERROR","Password Shadowing Conversion Failed\n" .
+ "$shadowTODO");
+ # exiting the subroutine.
+ return 0;
+ }
+
+ }
+
+ # convert the password file to a shadowed repository.
+ if (( -e $pwconv ) and ( -e $pwunconv ) and
+ ( &B_System("$echo \"yes\" | $pwconv","$pwunconv") ) ){
+ &B_TODO( "\n---------------------------------\nShadowing Password File:\n" .
+ "---------------------------------\n" .
+ "Your password file has been converted to use password shadowing.\n" .
+ "This version of password shadowing does not support any repository other\n" .
+ "than files. There can be no mention of nis, nisplus, compat, or dce\n" .
+ "in the passwd field of the \"$nsswitch_conf\" file.\n\n" );
+ } else {
+ &B_log("ERROR","Conversion to shadow mode failed. The system may require ".
+ "a patch to be capable of switching to shadow mode, or the ".
+ "system my be in a state where conversion is not possible.");
+ }
+}
+
+
+
+##########################################################################
+# &getSupportedSettings();
+# Manipulates %trustedParameter and %isSupportedSetting, file-scoped variables
+#
+# Reads the password policy support matrix, which in-turn gives Bastille the
+# places it should look for a given password policy setting.
+
+# Note the file was created like this so if could be maintained in an Excel(tm)
+# spreadsheet, to optimize reviewability. TODO: consider other formats
+
+# File Format:
+# HEADERS:<comment>,[<OS Version> <Mode> <Extensions>,]...
+# [
+# :<label>:<trusted equivalent>,,,,,,,,,,,,<comment>
+# <action> (comment), [<test value>,]...
+# ] ...
+# Example;
+# HEADERS:Information Source (trusted equiv),11.11 Standard no-SMSE,11.11 Trusted no-SMSE,11.11 Shadow no-SMSE,11.23 Standard no-SMSE,11.23 Trusted no-SMSE,11.23 Shadow no-SMSE,11.23 Standard SMSE,11.23 Shadow SMSE,11.23 Trusted SMSE,11.31 Trusted SMSE,11.31 Shadow SMSE,11.31 Standard SMSE,Other Exceptions
+#:ABORT_LOGIN_ON_MISSING_HOMEDIR,,,,,,,,,,,,,root
+#/etc/security.dsc (search),x,,xx,x,x,x,!,!,!,!,!,!,
+#/etc/default/security(search),y,y,y,y,y,y,y,y,y,y,y,y,
+#getprdef (execute with <Trusted Equiv> argument),x,x,x,x,x,x,x,x,x,x,x,x,
+
+###########################################################################
+our %trustedParameter = ();
+our %isSupportedSetting = ();
+
+sub getSupportedSettings() {
+
+ my $line; # For a config file line
+ my $linecount = 0;
+ my $currentsetting = "";
+ my @fields; # Fields in a given line
+ my @columns; #Column Definitions
+
+
+ &B_open(*SETTINGSFILE,&getGlobal('BFILE','AccountSecSupport'));
+ my @settingLines=<SETTINGSFILE>;
+ &B_close(*SETTINGSFILE);
+
+ #Remove blank-lines and comments
+ @settingLines = grep(!/^#/,@settingLines);
+ @settingLines = grep(!/^(\s*,+)*$/,@settingLines);
+
+ foreach $line (@settingLines) {
+ ++$linecount;
+ @fields = split(/,/,$line);
+ if ($line =~ /^Information Source:/) { #Sets up colums
+ my $fieldcount = 1; #Skipping first field
+ while ((defined($fields[$fieldcount])) and
+ ($fields[$fieldcount] =~ /\d+\.\d+/)){
+ my @subfields = split(/ /,$fields[$fieldcount]);
+ my $fieldsCount = @subfields;
+ if ($fieldsCount != 3){
+ &B_log("ERROR","Invalid subfield count: $fieldsCount in:".
+ &getGlobal('BFILE','AccountSecSupport') .
+ " line: $linecount and field: $fieldcount");
+ }
+ $columns[$fieldcount] = {OSVersion => $subfields[0],
+ Mode => $subfields[1],
+ Extension => $subfields[2] };
+ &B_log("DEBUG","Found Header Column, $columns[$fieldcount]{'OSVersion'}, ".
+ $columns[$fieldcount]{'Mode'} ." , " .
+ $columns[$fieldcount]{'Extension'});
+ ++$fieldcount;
+ } # New Account Seting ex:
+ } elsif ($line =~ /^:([^,:]+)(?::([^,]+))?/) { # :PASSWORD_WARNDAYS:expwarn,,,,,,,,,,,,
+ $currentsetting = $1;
+ if (defined($2)) {
+ $trustedParameter{"$currentsetting"}=$2;
+ }
+ &B_log("DEBUG","Found Current Setting: ". $currentsetting .
+ "/" . $trustedParameter{"$currentsetting"});
+ } elsif (($line =~ /(^[^, :\)\(]+)[^,]*,((?:(?:[!y?nx]|!!),)+)/) and #normal line w/ in setting ex:
+ ($currentsetting ne "")){ # security.dsc (search),x,x,x,x,x,!,!!,!,!,!,!,
+ my $placeToLook = $1;
+ my $fieldcount = 1; #Skip the first one, which we used in last line
+ while (defined($fields[$fieldcount])) {
+ &B_log("DEBUG","Setting $currentsetting : $columns[$fieldcount]{OSVersion} , ".
+ "$columns[$fieldcount]{Mode} , ".
+ "$columns[$fieldcount]{Extension} , ".
+ "$placeToLook, to $fields[$fieldcount]");
+ $isSupportedSetting{"$currentsetting"}
+ {"$columns[$fieldcount]{OSVersion}"}
+ {"$columns[$fieldcount]{Mode}"}
+ {"$columns[$fieldcount]{Extension}"}
+ {"$placeToLook"} =
+ $fields[$fieldcount];
+ ++$fieldcount;
+ }
+ } else {
+ if ($line !~ /^,*/) {
+ &B_log("ERROR","Incorrectly Formatted Line at ".
+ &getGlobal('BFILE','AccountSecSupport') . ": $linecount");
+ }
+ }
+ }
+}
+
+##########################################################################
+# &B_get_sec_value($param);
+# This subroutine finds the value for a given user policy parameter.
+# Specifically, it supports the parameters listed in the internal data structure
+
+# Return values:
+# 'Not Defined' if the value is not present or not uniquely defined.
+# $value if the value is present and unique
+#
+###########################################################################
+sub B_get_sec_value($) {
+ my $param=$_[0];
+
+ my $os_version;
+ if (&GetDistro =~ /^HP-UX\D*(\d+\.\d+)/ ){
+ $os_version=$1;
+ } else {
+ &B_log("ERROR","B_get_sec_value only supported on HP-UX");
+ return undef;
+ }
+# my $sec_dsc = &getGlobal('FILE', 'security.dsc');
+ my $sec_file = &getGlobal('FILE', 'security');
+ my $getprdef = &getGlobal('BIN','getprdef');
+ my $getprpw = &getGlobal('BIN','getprpw');
+ my $userdbget = &getGlobal('BIN','userdbget');
+ my $passwd = &getGlobal('BIN','passwd');
+
+ my $sec_flags = "";
+ my @sec_settings=();
+ my $user_sec_setting="";
+
+ my $security_mode="Standard";
+ my $security_extension="no-SMSE";
+
+ &B_log("DEBUG","Entering get_sec_value for: $param");
+
+ sub isok ($) { # Locally-scoped subroutine, takes supported-matrix entry as argument
+ my $supportedMatrixEntry = $_[0];
+
+ if ($supportedMatrixEntry =~ /!/) { #Matrix Entry for "Documented and/or tested"
+ &B_log("DEBUG","isOk TRUE: $supportedMatrixEntry");
+ return 1;
+ } else {
+ &B_log("DEBUG","isOk FALSE: $supportedMatrixEntry");
+ return 0; #FALSE
+ }
+ } #end local subroutine
+
+ #Get Top Array item non-destructively
+ sub getTop (@) {
+ my @incomingArray = @_;
+ my $topval = pop(@incomingArray);
+ push(@incomingArray,$topval); #Probably redundant, but left in just in case.
+ return $topval;
+ }
+
+ sub ifExistsPushOnSecSettings($$) {
+ my $sec_settings = $_[0];
+ my $pushval = $_[1];
+
+ if ($pushval ne ""){
+ push (@$sec_settings, $pushval);
+ }
+ }
+
+ #prpw and prdef both use "YES" instead of "1" like the other settings.
+ sub normalizePolicy($){
+ my $setting = $_[0];
+
+ $setting =~ s/YES/1/;
+ $setting =~ s/NO/1/;
+
+ return $setting;
+ }
+
+
+
+ if ((%trustedParameter == ()) or (%isSupportedSetting == ())) {
+ # Manipulates %trustedParameter and %isSupportedSetting
+ &getSupportedSettings;
+ }
+
+ #First determine the security mode
+ my $shadowFile = &getGlobal("FILE","shadow");
+ my $passwdFile = &getGlobal("FILE","passwd");
+
+ if (&isSystemTrusted) {
+ $security_mode = 'Trusted';
+ } elsif ((-e $shadowFile) and #check file exist, and that passwd has no non-"locked" accounts
+ (not(&B_match_line($passwdFile,'^[^\:]+:[^:]*[^:*x]')))) {
+ $security_mode = 'Shadow';
+ } else {
+ $security_mode = 'Standard';
+ }
+ if (&isTrustedMigrationAvailable) {
+ $security_extension = 'SMSE';
+ } else {
+ $security_extension = 'no-SMSE';
+ }
+ &B_log("DEBUG","Security mode: $security_mode extension: $security_extension");
+ # Now look up the value from each applicable database, from highest precedence
+ # to lowest:
+ &B_log("DEBUG","Checking $param in userdbget");
+ if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
+ {$security_extension}{"userdbget_-a"})) {
+ &ifExistsPushOnSecSettings(\@sec_settings,
+ &B_getValueFromString('\w+\s+\w+=(\S+)',
+ &B_Backtick("$userdbget -a $param")));
+ &B_log("DEBUG", $param . ":userdbget setting: ". &getTop(@sec_settings));
+ }
+ &B_log("DEBUG","Checking $param in passwd");
+ if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
+ {$security_extension}{"passwd_-sa"})) {
+ if ($param eq "PASSWORD_MINDAYS") {
+ &ifExistsPushOnSecSettings(\@sec_settings,
+ &B_getValueFromString('(?:\w+\s+){2}[\d\/]+\s+(\d+)\s+\d+',
+ &B_Backtick("$passwd -s -a")));
+ } elsif ($param eq "PASSWORD_MAXDAYS") {
+ &ifExistsPushOnSecSettings(\@sec_settings,
+ &B_getValueFromString('(?:\w+\s+){2}[\d\/]+\s+\d+\s+(\d+)',
+ &B_Backtick("$passwd -s -a")));
+ } elsif ($param eq "PASSWORD_WARNDAYS") {
+ &ifExistsPushOnSecSettings(\@sec_settings,
+ &B_getValueFromString('(?:\w+\s+){2}[\d\/]+(?:\s+\d+){2}\s+(\d+)',
+ &B_Backtick("$passwd -s -a")));
+ }
+ &B_log("DEBUG", $param . ":passwd -sa setting: ". &getTop(@sec_settings));
+ }
+ &B_log("DEBUG","Checking $param in get prpw");
+ if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
+ {$security_extension}{"getprpw"})) {
+ my $logins = &getGlobal("BIN","logins");
+ my @userArray = split(/\n/,`$logins`);
+ my $userParamVals = '';
+ foreach my $rawuser (@userArray) {
+ $rawuser =~ /^(\S+)/;
+ my $user = $1;
+ my $nextParamVal=&B_Backtick("$getprpw -l -m $trustedParameter{$param} $user");
+ $nextParamVal =~ s/\w*=(-*[\w\d]*)/$1/;
+ if ($nextParamVal != -1) { #Don't count users for which the local DB is undefined
+ $userParamVals .= $user . "::::" . $nextParamVal ."\n";
+ }
+ } #Note getValueFromStrings deals with duplicates, returning "Not Unigue"
+ my $policySetting = &B_getValueFromString('::::(\S+)',"$userParamVals");
+ &ifExistsPushOnSecSettings (\@sec_settings, &normalizePolicy($policySetting));
+ &B_log("DEBUG", $param . ":prpw setting: ". &getTop(@sec_settings));
+ }
+ &B_log("DEBUG","Checking $param in get prdef");
+ if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
+ {$security_extension}{"getprdef"})) {
+ $_ = &B_Backtick ("$getprdef -m " . $trustedParameter{$param});
+ /\S+=(\S+)/;
+ my $policySetting = $1;
+ &ifExistsPushOnSecSettings(\@sec_settings, &normalizePolicy($policySetting));
+ &B_log("DEBUG", $param . ":prdef setting: ". &getTop(@sec_settings));
+
+ }
+ &B_log("DEBUG","Checking $param in default security");
+ if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
+ {$security_extension}{"/etc/default/security"})) {
+ &ifExistsPushOnSecSettings(\@sec_settings,&B_getValueFromFile('^\s*'. $param .
+ '\s*=\s*([^\s#]+)\s*$', $sec_file));
+ &B_log("DEBUG", $param . ":default setting: ". &getTop(@sec_settings));
+ }
+ #Commented below code in 3.0 release to avoid implication that bastille
+ #had ever set these values explicitly, and the implications to runnable
+ #config files where Bastille would then apply the defaults as actual policy
+ #with possible conversion to shadow or similar side-effect.
+
+# &B_log("DEBUG","Checking $param in security.dsc");
+ #security.dsc, only added in if valid for OS/mode/Extension, and nothing else
+ #is defined (ie: @sec_settings=0)
+# if ((&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
+# {$security_extension}{"/etc/security.dsc"})) and (@sec_settings == 0)) {
+# &ifExistsPushOnSecSettings(\@sec_settings, &B_getValueFromFile('^' . $param .
+# ';(?:[-\w/]*;){2}([-\w/]+);', $sec_dsc));
+# &B_log("DEBUG", $param . ":security.dsc: ". &getTop(@sec_settings));
+# }
+
+ # Return what we found
+ my $last_setting=undef;
+ my $current_setting=undef;
+ while (@sec_settings > 0) {
+ $current_setting = pop(@sec_settings);
+ &B_log("DEBUG","Comparing $param configuration for identity: " .
+ $current_setting);
+ if ((defined($current_setting)) and ($current_setting ne '')) {
+ if (not(defined($last_setting))){
+ $last_setting=$current_setting;
+ } elsif (($last_setting ne $current_setting) or
+ ($current_setting eq 'Not Unique')){
+ &B_log("DEBUG","$param setting not unique.");
+ return 'Not Unique'; # Inconsistent state found, return 'Not Unique'
+ }
+ }
+ }
+ if ((not(defined($last_setting))) or ($last_setting eq '')) {
+ return undef;
+ } else {
+ return $last_setting;
+ }
+
+} #End B_get_sec_value
+
+sub secureIfNoNameService($){
+ my $retval = $_[0];
+
+ if (&isUsingRemoteNameService) {
+ return MANUAL();
+ } else {
+ return $retval;
+ }
+}
+
+#Specifically for cleartext protocols like NIS, which are not "secure"
+sub isUsingRemoteNameService(){
+
+ if (&remoteServiceCheck('nis|nisplus|dce') == SECURE_CAN_CHANGE()){
+ return 0; #false
+ } else {
+ return 1;
+ }
+}
+
+
+
+###########################################
+## This is a wrapper for two functions that
+## test the existence of nis-like configurations
+## It is used by both the front end test and the back-end run
+##############################################
+sub remoteServiceCheck($){
+ my $regex = $_[0];
+
+ my $nsswitch_conf = &getGlobal('FILE',"nsswitch.conf");
+ my $passwd = &getGlobal('FILE',"passwd");
+
+ # check the file for nis usage.
+ if (-e $nsswitch_conf) {
+ if (&B_match_line($nsswitch_conf, '^\s*passwd:.*('. $regex . ')')) {
+ return NOTSECURE_CAN_CHANGE();
+ } elsif ((&B_match_line($nsswitch_conf, '^\s*passwd:.*(compat)')) and
+ (&B_match_line($passwd, '^\s*\+'))) {
+ return NOTSECURE_CAN_CHANGE(); # true
+ }
+ } elsif ((&B_match_line($passwd, '^\s*\+'))) {
+ return NOTSECURE_CAN_CHANGE();
+ }
+
+ my $oldnisdomain=&B_get_rc("NIS_DOMAIN");
+ if ((($oldnisdomain eq "") or ($oldnisdomain eq '""')) and (&checkServiceOnHPUX('nis.client'))){
+ return SECURE_CAN_CHANGE();
+ }
+ return NOTSECURE_CAN_CHANGE();
+}
+
+#############################################
+# remoteNISPlusServiceCheck
+# test the existence of nis+ configuration
+#############################################
+sub remoteNISPlusServiceCheck () {
+
+ my $nsswitch_conf = &getGlobal('FILE',"nsswitch.conf");
+
+ # check the file for nis+ usage.
+ if (-e $nsswitch_conf) {
+ if (&B_match_line($nsswitch_conf, 'nisplus')) {
+ return NOTSECURE_CAN_CHANGE();
+ }
+ }
+
+ return &checkServiceOnHPUX('nisp.client');
+}
+
+
+##########################################################################
+# This subroutine creates nsswitch.conf file if the file not exists,
+# and then append serveral services into the file if the service not
+# exists in the file.
+##########################################################################
+sub B_create_nsswitch_file ($) {
+ my $regex = $_[0];
+
+ my $nsswitch = &getGlobal('FILE',"nsswitch.conf");
+
+ if( ! -f $nsswitch ) {
+ &B_create_file($nsswitch);
+ # we don't need to revert the permissions change because we just
+ # created the file
+ chmod(0444, $nsswitch);
+
+ &B_append_line($nsswitch,'\s*passwd:', "passwd: $regex\n");
+ &B_append_line($nsswitch,'\s*group:', "group: $regex\n");
+ &B_append_line($nsswitch,'\s*hosts:', "hosts: $regex\n");
+ &B_append_line($nsswitch,'\s*networks:', "networks: $regex\n");
+ &B_append_line($nsswitch,'\s*protocols:', "protocols: $regex\n");
+ &B_append_line($nsswitch,'\s*rpc:', "rpc: $regex\n");
+ &B_append_line($nsswitch,'\s*publickey:', "publickey: $regex\n");
+ &B_append_line($nsswitch,'\s*netgroup:', "netgroup: $regex\n");
+ &B_append_line($nsswitch,'\s*automount:', "automount: $regex\n");
+ &B_append_line($nsswitch,'\s*aliases:', "aliases: $regex\n");
+ &B_append_line($nsswitch,'\s*services:', "services: $regex\n");
+ }
+}
+
+1;
+
diff --git a/recipes-security/bastille/files/Miscellaneous.pm b/recipes-security/bastille/files/Miscellaneous.pm
new file mode 100644
index 0000000..b3bdf10
--- /dev/null
+++ b/recipes-security/bastille/files/Miscellaneous.pm
@@ -0,0 +1,166 @@
+package Bastille::API::Miscellaneous;
+use strict;
+
+use File::Path;
+use Bastille::API;
+use Bastille::API::HPSpecific;
+use Bastille::API::FileContent;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(
+PrepareToRun
+B_is_package_installed
+);
+our @EXPORT = @EXPORT_OK;
+
+
+###########################################################################
+#
+# PrepareToRun sets up Bastille to run. It checks the ARGV array for
+# special options and runs ConfigureForDistro to set necessary file
+# locations and other global variables.
+#
+###########################################################################
+
+sub PrepareToRun {
+
+ # Make sure we're root!
+ if ( $> != 0 ) {
+ &B_log("ERROR","Bastille must run as root!\n");
+ exit(1);
+ }
+
+
+ # Make any directories that don't exist...
+ foreach my $dir (keys %GLOBAL_BDIR) {
+ my $BdirPath = $GLOBAL_BDIR{$dir};
+ if ( $BdirPath =~ /^\s*\// ) { #Don't make relative directories
+ mkpath ($BdirPath,0,0700);
+ }
+ }
+
+ if(&GetDistro =~ "^HP-UX") {
+ &B_check_system;
+ }
+
+ &B_log("ACTION","\n########################################################\n" .
+ "# Begin Bastille Run #\n" .
+ "########################################################\n\n");
+
+ #read sum file if it exists.
+ &B_read_sums;
+
+
+# No longer necessary as flags are no longer in sum file, and sums are
+# are now checked "real time"
+
+ # check the integrity of the files listed
+# for my $file (sort keys %GLOBAL_SUM) {
+# &B_check_sum($file);
+# }
+ # write out the newly flagged sums
+# &B_write_sums;
+
+
+}
+
+
+
+###########################################################################
+# &B_is_package_installed($package);
+#
+# This function checks for the existence of the package named.
+#
+# TODO: Allow $package to be an expression.
+# TODO: Allow optional $version, $release, $epoch arguments so we can
+# make sure that the given package is at least as recent as some
+# given version number.
+#
+# scalar return values:
+# 0: $package is not installed
+# 1: $package is installed
+###########################################################################
+
+sub B_is_package_installed($) {
+ no strict;
+ my $package = $_[0];
+# Create a "global" variable with values scoped to this function
+# We do this to avoid having to repeatedly swlist/rpm
+# when we run B_is_package_installed
+local %INSTALLED_PACKAGE_LIST;
+
+ my $distro = &GetDistro;
+ if ($distro =~ /^HP-UX/) {
+ if (&checkProcsForService('swagent','ignore_warning') == SECURE_CANT_CHANGE()) {
+ &B_log("WARNING","Software Distributor Agent(swagent) is not running. Can not tell ".
+ "if package: $package is installed or not. Bastille will assume not. ".
+ "If the package is actually installed, Bastille may report or configure incorrectly.".
+ "To use Bastille-results as-is, please check to ensure $package is not installed, ".
+ "or re-run with the swagent running to get correct results.");
+ return 0; #FALSE
+ }
+ my $swlist=&getGlobal('BIN','swlist');
+ if (%INSTALLED_PACKAGE_LIST == () ) { # re-use prior results
+ if (open(SWLIST, "$swlist -a state -l fileset |")) {
+ while (my $line = <SWLIST>){
+ if ($line =~ /^ {2}\S+\.(\S+)\s*(\w+)/) {
+ $INSTALLED_PACKAGE_LIST{$1} = $2;
+ }
+ }
+ close SWLIST;
+ } else {
+ &B_log("ERROR","B_is_package_installed was unable to run the swlist command: $swlist,\n");
+ return FALSE;
+ }
+ }
+ # Now find the entry
+ if ($INSTALLED_PACKAGE_LIST{$package} == 'configured') {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ } #End HP-UX Section
+ # This routine only works on RPM-based distros: Red Hat, Fedora, Mandrake and SuSE
+ elsif ( ($distro !~ /^RH/) and ($distro !~ /^MN/) and($distro !~ /^SE/) ) {
+ return 0;
+ } else { #This is a RPM-based distro
+ # Run an rpm command -- librpm is extremely messy, dynamic and not
+ # so much a perl thing. It's actually barely a C/C++ thing...
+ if (open RPM,"rpm -q $package") {
+ # We should get only one line back, but let's parse a few
+ # just in case.
+ my @lines = <RPM>;
+ close RPM;
+ #
+ # This is what we're trying to parse:
+ # $ rpm -q jay
+ # package jay is not installed
+ # $ rpm -q bash
+ # bash-2.05b-305.1
+ #
+
+ foreach $line (@lines) {
+ if ($line =~ /^package\s$package\sis\snot\sinstalled/) {
+ return 0;
+ }
+ elsif ($line =~ /^$package\-/) {
+ return 1;
+ }
+ }
+
+ # If we've read every line without finding one of these, then
+ # our parsing is broken
+ &B_log("ERROR","B_is_package_installed was unable to find a definitive RPM present or not present line.\n");
+ return 0;
+ } else {
+ &B_log("ERROR","B_is_package_installed was unable to run the RPM command,\n");
+ return 0;
+ }
+ }
+}
+
+
+
+1;
+
diff --git a/recipes-security/bastille/files/ServiceAdmin.pm b/recipes-security/bastille/files/ServiceAdmin.pm
new file mode 100644
index 0000000..879223a
--- /dev/null
+++ b/recipes-security/bastille/files/ServiceAdmin.pm
@@ -0,0 +1,690 @@
+package Bastille::API::ServiceAdmin;
+use strict;
+
+use Bastille::API;
+
+use Bastille::API::HPSpecific;
+use Bastille::API::FileContent;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(
+B_chkconfig_on
+B_chkconfig_off
+B_service_start
+B_service_stop
+B_service_restart
+B_is_service_off
+checkServiceOnLinux
+remoteServiceCheck
+remoteNISPlusServiceCheck
+B_create_nsswitch_file
+);
+our @EXPORT = @EXPORT_OK;
+
+
+#######
+# &B_chkconfig_on and &B_chkconfig_off() are great for systems that didn't use
+# a more modern init system. This is a bit of a problem on Fedora, though,
+# which used upstart from Fedora 9 to Fedora 14, then switched to a new
+# Red Hat-created system called systemd for Fedora 15 and 16 (so far).
+# OpenSUSE also moved to systemd, starting with 12.1. Version 11.4 did not
+# use systemd.
+# It is also a problem on Ubuntu, starting at version 6.10, where they also
+# used upstart.
+#####
+
+
+
+
+###########################################################################
+# &B_chkconfig_on ($daemon_name) creates the symbolic links that are
+# named in the "# chkconfig: ___ _ _ " portion of the init.d files. We
+# need this utility, in place of the distro's chkconfig, because of both
+# our need to add revert functionality and our need to harden distros that
+# are not mounted on /.
+#
+# It uses the following global variables to find the links and the init
+# scripts, respectively:
+#
+# &getGlobal('DIR', "rcd") -- directory where the rc_.d subdirs can be found
+# &getGlobal('DIR', "initd") -- directory the rc_.d directories link to
+#
+# Here an example of where you might use this:
+#
+# You'd like to tell the system to run the firewall at boot:
+# B_chkconfig_on("bastille-firewall")
+#
+###########################################################################
+
+# PW: Blech. Copied B_chkconfig_off() and changed a few things,
+# then changed a few more things....
+
+sub B_chkconfig_on {
+
+ my $startup_script=$_[0];
+ my $retval=1;
+
+ my $chkconfig_line;
+ my ($runlevelinfo,@runlevels);
+ my ($start_order,$stop_order,$filetolink);
+
+ &B_log("ACTION","# chkconfig_on enabling $startup_script\n");
+
+ # In Debian system there is no chkconfig script, run levels are checked
+ # one by one (jfs)
+ if (&GetDistro =~/^DB.*/) {
+ $filetolink = &getGlobal('DIR', "initd") . "/$startup_script";
+ if (-x $filetolink)
+ {
+ foreach my $level ("0","1","2","3","4","5","6" ) {
+ my $link = '';
+ $link = &getGlobal('DIR', "rcd") . "/rc" . "$level" . ".d/K50" . "$startup_script";
+ $retval=symlink($filetolink,$link);
+ }
+ }
+ return $retval;
+ }
+ #
+ # On SUSE, chkconfig-based rc scripts have been replaced with a whole different
+ # system. chkconfig on SUSE is actually a shell script that does some stuff and then
+ # calls insserv, their replacement.
+ #
+
+ if (&GetDistro =~ /^SE/) {
+ # only try to chkconfig on if init script is found
+ if ( -e (&getGlobal('DIR', "initd") . "/$startup_script") ) {
+ $chkconfig_line=&getGlobal('BIN','chkconfig');
+ &B_System("$chkconfig_line $startup_script on", "$chkconfig_line $startup_script off");
+ # chkconfig doesn't take affect until reboot, need to restart service also
+ B_service_restart("$startup_script");
+ return 1; #success
+ }
+ return 0; #failure
+ }
+
+ #
+ # Run through the init script looking for the chkconfig line...
+ #
+ $retval = open CHKCONFIG,&getGlobal('DIR', "initd") . "/$startup_script";
+ unless ($retval) {
+ &B_log("ACTION","# Didn't chkconfig_on $startup_script because we couldn't open " . &getGlobal('DIR', "initd") . "/$startup_script\n");
+ }
+ else {
+
+ READ_LOOP:
+ while (my $line=<CHKCONFIG>) {
+
+ # We're looking for lines like this one:
+ # # chkconfig: 2345 10 90
+ # OR this
+ # # chkconfig: - 10 90
+
+ if ($line =~ /^#\s*chkconfig:\s*([-\d]+)\s*(\d+)\s*(\d+)/ ) {
+ $runlevelinfo = $1;
+ $start_order = $2;
+ $stop_order = $3;
+ # handle a run levels arg of '-'
+ if ( $runlevelinfo eq '-' ) {
+ &B_log("ACTION","chkconfig_on saw '-' for run levels for \"$startup_script\", is defaulting to levels 3,4,5\n");
+ $runlevelinfo = '345';
+ }
+ @runlevels = split(//,$runlevelinfo);
+ # make sure the orders have 2 digits
+ $start_order =~ s/^(\d)$/0$1/;
+ $stop_order =~ s/^(\d)$/0$1/;
+ last READ_LOOP;
+ }
+ }
+ close CHKCONFIG;
+
+ # Do we have what we need?
+ if ( (scalar(@runlevels) < 1) || (! $start_order =~ /^\d{2}$/) || (! $stop_order =~ /^\d{2}$/) ) {
+ # problem
+ &B_log("ERROR","# B_chkconfig_on $startup_script failed -- no valid run level/start/stop info found\n");
+ return(-1);
+ }
+
+ # Now, run through creating symlinks...
+ &B_log("ACTION","# chkconfig_on will use run levels ".join(",",@runlevels)." for \"$startup_script\" with S order $start_order and K order $stop_order\n");
+
+ $retval=0;
+ # BUG: we really ought to readdir() on &getGlobal('DIR', "rcd") to get all levels
+ foreach my $level ( "0","1","2","3","4","5","6" ) {
+ my $link = '';
+ # we make K links in run levels not specified in the chkconfig line
+ $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/K$stop_order" . $startup_script;
+ my $klink = $link;
+ # now we see if this is a specified run level; if so, make an S link
+ foreach my $markedlevel ( @runlevels ) {
+ if ( $level == $markedlevel) {
+ $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/S$start_order" . $startup_script;
+ }
+ }
+ my $target = &getGlobal('DIR', "initd") ."/" . $startup_script;
+ my $local_return;
+
+ if ( (-e "$klink") && ($klink ne $link) ) {
+ # there's a K link, but this level needs an S link
+ unless ($GLOBAL_LOGONLY) {
+ $local_return = unlink("$klink");
+ if ( ! $local_return ) {
+ # unlinking old, bad $klink failed
+ &B_log("ERROR","Unlinking $klink failed\n");
+ } else {
+ &B_log("ACTION","Removed link $klink\n");
+ # If we removed the link, add a link command to the revert file
+ &B_revert_log (&getGlobal('BIN','ln') . " -s $target $klink\n");
+ } # close what to do if unlink works
+ } # if not GLOBAL_LOGONLY
+ } # if $klink exists and ne $link
+
+ # OK, we've disposed of any old K links, make what we need
+ if ( (! ( -e "$link" )) && ($link ne '') ) {
+ # link doesn't exist and the start/stop number is OK; make it
+ unless ($GLOBAL_LOGONLY) {
+ # create the link
+ $local_return = &B_symlink($target,$link);
+ if ($local_return) {
+ $retval++;
+ &B_log("ACTION","Created link $link\n");
+ } else {
+ &B_log("ERROR","Couldn't create $link when trying to chkconfig on $startup_script\n");
+ }
+ }
+
+ } # link doesn't exist
+ } # foreach level
+
+ }
+
+ if ($retval < @runlevels) {
+ $retval=0;
+ }
+
+ $retval;
+
+}
+
+
+###########################################################################
+# &B_chkconfig_off ($daemon_name) deletes the symbolic links that are
+# named in the "# chkconfig: ___ _ _ " portion of the init.d files. We
+# need this utility, in place of the distro's chkconfig, because of both
+# our need to add revert functionality and our need to harden distros that
+# are not mounted on /.
+#
+# chkconfig allows for a REVERT of its work by writing to an executable
+# file &getGlobal('BFILE', "removed-symlinks").
+#
+# It uses the following global variables to find the links and the init
+# scripts, respectively:
+#
+# &getGlobal('DIR', "rcd") -- directory where the rc_.d subdirs can be found
+# &getGlobal('DIR', "initd") -- directory the rc_.d directories link to
+#
+# Here an example of where you might use this:
+#
+# You'd like to tell stop running sendmail in daemon mode on boot:
+# B_chkconfig_off("sendmail")
+#
+###########################################################################
+
+
+
+sub B_chkconfig_off {
+
+ my $startup_script=$_[0];
+ my $retval=1;
+
+ my $chkconfig_line;
+ my @runlevels;
+ my ($start_order,$stop_order,$filetolink);
+
+ if (&GetDistro =~/^DB.*/) {
+ $filetolink = &getGlobal('DIR', "initd") . "/$startup_script";
+ if (-x $filetolink)
+ {
+ # Three ways to do this in Debian:
+ # 1.- have the initd script set to 600 mode
+ # 2.- Remove the links in rcd (re-installing the package
+ # will break it)
+ # 3.- Use update-rc.d --remove (same as 2.)
+ # (jfs)
+ &B_chmod(0600,$filetolink);
+ $retval=6;
+
+ # The second option
+ #foreach my $level ("0","1","2","3","4","5","6" ) {
+ #my $link = '';
+ #$link = &getGlobal('DIR', "rcd") . "/rc" . "$level" . ".d/K50" . "$startup_script";
+ #unlink($link);
+ #}
+ }
+ }
+
+ #
+ # On SUSE, chkconfig-based rc scripts have been replaced with a whole different
+ # system. chkconfig on SUSE is actually a shell script that does some stuff and then
+ # calls insserv, their replacement.
+ #
+ elsif (&GetDistro =~ /^SE/) {
+ # only try to chkconfig off if init script is found
+ if ( -e (&getGlobal('DIR', "initd") . "/$startup_script") ) {
+ $chkconfig_line=&getGlobal('BIN','chkconfig');
+ &B_System("$chkconfig_line $startup_script on", "$chkconfig_line $startup_script off");
+ # chkconfig doesn't take affect until reboot, need to stop service
+ # since expectation is that the daemons are disabled even without a reboot
+ B_service_stop("$startup_script");
+ return 1; #success
+ }
+ return 0; #failure
+ }
+ else {
+
+ # Run through the init script looking for the chkconfig line...
+
+
+ $retval = open CHKCONFIG,&getGlobal('DIR', "initd") . "/$startup_script";
+ unless ($retval) {
+ &B_log("ACTION","Didn't chkconfig_off $startup_script because we couldn't open " . &getGlobal('DIR', "initd") . "/$startup_script\n");
+ }
+ else {
+
+ READ_LOOP:
+ while (my $line=<CHKCONFIG>) {
+
+ # We're looking for lines like this one:
+ # # chkconfig: 2345 10 90
+
+ if ($line =~ /^#\s*chkconfig:\s*([-\d]+)\s*(\d+)\s*(\d+)/ ) {
+ @runlevels=split //,$1;
+ $start_order=$2;
+ $stop_order=$3;
+
+
+ # Change single digit run levels to double digit -- otherwise,
+ # the alphabetic ordering chkconfig depends on fails.
+ if ($start_order =~ /^\d$/ ) {
+ $start_order = "0" . $start_order;
+ &B_log("ACTION","chkconfig_off converted start order to $start_order\n");
+ }
+ if ($stop_order =~ /^\d$/ ) {
+ $stop_order = "0" . $stop_order;
+ &B_log("ACTION","chkconfig_off converted stop order to $stop_order\n");
+ }
+
+ last READ_LOOP;
+ }
+ }
+ close CHKCONFIG;
+
+ # If we never found a chkconfig line, can we just run through all 5
+ # rcX.d dirs from 1 to 5...?
+
+ # unless ( $start_order and $stop_order ) {
+ # @runlevels=("1","2","3","4","5");
+ # $start_order = "*"; $stop_order="*";
+ # }
+
+ # Now, run through removing symlinks...
+
+
+
+ $retval=0;
+
+ # Handle the special case that the run level specified is solely "-"
+ if ($runlevels[0] =~ /-/) {
+ @runlevels = ( "0","1","2","3","4","5","6" );
+ }
+
+ foreach my $level ( @runlevels ) {
+ my $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/S$start_order" . $startup_script;
+ my $new_link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/K$stop_order" . $startup_script;
+ my $target = &getGlobal('DIR', "initd") ."/" . $startup_script;
+ my $local_return;
+
+
+ # Replace the S__ link in this level with a K__ link.
+ if ( -e $link ) {
+ unless ($GLOBAL_LOGONLY) {
+ $local_return=unlink $link;
+ if ($local_return) {
+ $local_return=symlink $target,$new_link;
+ unless ($local_return) {
+ &B_log("ERROR","Linking $target to $new_link failed.\n");
+ }
+ }
+ else { # unlinking failed
+ &B_log("ERROR","Unlinking $link failed\n");
+ }
+
+ }
+ if ($local_return) {
+ $retval++;
+ &B_log("ACTION","Removed link $link\n");
+
+ #
+ # If we removed the link, add a link command to the revert file
+ # Write out the revert information for recreating the S__
+ # symlink and deleting the K__ symlink.
+ &B_revert_log(&getGlobal('BIN',"ln") . " -s $target $link\n");
+ &B_revert_log(&getGlobal('BIN',"rm") . " -f $new_link\n");
+ }
+ else {
+ &B_log("ERROR","B_chkconfig_off $startup_script failed\n");
+ }
+
+ }
+ } # foreach
+
+ } # else-unless
+
+ } # else-DB
+ if ($retval < @runlevels) {
+ $retval=0;
+ }
+
+ $retval;
+
+}
+
+
+###########################################################################
+# &B_service_start ($daemon_name)
+# Starts service on RedHat/SUSE-based Linux distributions which have the
+# service command:
+#
+# service $daemon_name start
+#
+# Other Linux distros that also support this method of starting
+# services can be added to use this function.
+#
+# Here an example of where you might use this:
+#
+# You'd like to tell the system to start the vsftpd daemon:
+# &B_service_start("vsftpd")
+#
+# Uses &B_System in HP_API.pm
+# To match how the &B_System command works this method:
+# returns 1 on success
+# returns 0 on failure
+###########################################################################
+
+sub B_service_start {
+
+ my $daemon=$_[0];
+
+ if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
+ (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
+ &B_log("ERROR","Tried to call service_start on a system lacking a service command! Internal Bastille error.");
+ return undef;
+ }
+
+ # only start service if init script is found
+ if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
+ &B_log("ACTION","# service_start enabling $daemon\n");
+
+ my $service_cmd=&getGlobal('BIN', 'service');
+ if ($service_cmd) {
+ # Start the service,
+ # Also provide &B_System revert command
+
+ return (&B_System("$service_cmd $daemon start",
+ "$service_cmd $daemon stop"));
+ }
+ }
+
+ # init script not found, do not try to start, return failure
+ return 0;
+}
+
+###########################################################################
+# &B_service_stop ($daemon_name)
+# Stops service on RedHat/SUSE-based Linux distributions which have the
+# service command:
+#
+# service $daemon_name stop
+#
+# Other Linux distros that also support this method of starting
+# services can be added to use this function.
+# Stops service.
+#
+#
+# Here an example of where you might use this:
+#
+# You'd like to tell the system to stop the vsftpd daemon:
+# &B_service_stop("vsftpd")
+#
+# Uses &B_System in HP_API.pm
+# To match how the &B_System command works this method:
+# returns 1 on success
+# returns 0 on failure
+###########################################################################
+
+sub B_service_stop {
+
+ my $daemon=$_[0];
+
+ if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
+ (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
+ &B_log("ERROR","Tried to call service_stop on a system lacking a service command! Internal Bastille error.");
+ return undef;
+ }
+
+ # only stop service if init script is found
+ if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
+ &B_log("ACTION","# service_stop disabling $daemon\n");
+
+ my $service_cmd=&getGlobal('BIN', 'service');
+ if ($service_cmd) {
+
+ # Stop the service,
+ # Also provide &B_System revert command
+
+ return (&B_System("$service_cmd $daemon stop",
+ "$service_cmd $daemon start"));
+ }
+ }
+
+ # init script not found, do not try to stop, return failure
+ return 0;
+}
+
+
+###########################################################################
+# &B_service_restart ($daemon_name)
+# Restarts service on RedHat/SUSE-based Linux distributions which have the
+# service command:
+#
+# service $daemon_name restart
+#
+# Other Linux distros that also support this method of starting
+# services can be added to use this function.
+#
+# Here an example of where you might use this:
+#
+# You'd like to tell the system to restart the vsftpd daemon:
+# &B_service_restart("vsftpd")
+#
+# Uses &B_System in HP_API.pm
+# To match how the &B_System command works this method:
+# returns 1 on success
+# returns 0 on failure
+###########################################################################
+
+sub B_service_restart {
+
+ my $daemon=$_[0];
+
+ if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
+ (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
+ &B_log("ERROR","Tried to call service_restart on a system lacking a service command! Internal Bastille error.");
+ return undef;
+ }
+
+ # only restart service if init script is found
+ if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
+ &B_log("ACTION","# service_restart re-enabling $daemon\n");
+
+ my $service_cmd=&getGlobal('BIN', 'service');
+ if ($service_cmd) {
+
+ # Restart the service
+ return (&B_System("$service_cmd $daemon restart",
+ "$service_cmd $daemon restart"));
+ }
+ }
+
+ # init script not found, do not try to restart, return failure
+ return 0;
+}
+
+###########################################################################
+# &B_is_service_off($;$)
+#
+# Runs the specified test to determine whether or not the question should
+# be answered.
+#
+# return values:
+# NOTSECURE_CAN_CHANGE()/0: service is on
+# SECURE_CANT_CHANGE()/1: service is off
+# undef: test is not defined
+###########################################################################
+
+sub B_is_service_off ($){
+ my $service=$_[0];
+
+ if(&GetDistro =~ "^HP-UX"){
+ #die "Why do I think I'm on HPUX?!\n";
+ return &checkServiceOnHPUX($service);
+ }
+ elsif ( (&GetDistro =~ "^RH") || (&GetDistro =~ "^SE") ) {
+ return &checkServiceOnLinux($service);
+ }
+ else {
+ &B_log("DEBUG","B_is_service off called for unsupported OS");
+ # not yet implemented for other distributions of Linux
+ # when GLOBAL_SERVICE, GLOBAL_SERVTYPE and GLOBAL_PROCESS are filled
+ # in for Linux, then
+ # at least inetd and inittab services should be similar to the above,
+ # whereas chkconfig would be used on some Linux distros to determine
+ # if non-inetd/inittab services are running at boot time. Looking at
+ # processes should be similar.
+ return undef;
+ }
+}
+
+###########################################################################
+# &checkServiceOnLinux($service);
+#
+# Checks if the given service is running on a Linux system. This is
+# called by B_is_Service_Off(), which is the function that Bastille
+# modules should call.
+#
+# Return values:
+# NOTSECURE_CAN_CHANGE() if the service is on
+# SECURE_CANT_CHANGE() if the service is off
+# undef if the state of the service cannot be determined
+#
+###########################################################################
+sub checkServiceOnLinux($) {
+ my $service=$_[0];
+
+ # get the list of parameters which could be used to initiate the service
+ # (could be in /etc/rc.d/rc?.d, /etc/inetd.conf, or /etc/inittab, so we
+ # check all of them)
+
+ my @params = @{ &getGlobal('SERVICE', $service) };
+ my $chkconfig = &getGlobal('BIN', 'chkconfig');
+ my $grep = &getGlobal('BIN', 'grep');
+ my $inittab = &getGlobal('FILE', 'inittab');
+ my $serviceType = &getGlobal('SERVTYPE', $service);;
+
+ # A kludge to get things running because &getGlobal('SERVICE' doesn't
+ # return the expected values.
+ @params = ();
+ push (@params, $service);
+
+ foreach my $param (@params) {
+ &B_log("DEBUG","Checking to see if service $service is off.\n");
+
+ if ($serviceType =~ /rc/) {
+ my $on = &B_Backtick("$chkconfig --list $param 2>&1");
+ if ($on =~ /^$param:\s+unknown/) {
+ # This service isn't installed on the system
+ return NOT_INSTALLED();
+ }
+ if ($on =~ /^error reading information on service $param: No such file or directory/) {
+ # This service isn't installed on the system
+ return NOT_INSTALLED();
+ }
+ if ($on =~ /^error/) {
+ # This probably
+ &B_log("DEBUG","chkconfig returned: $param=$on\n");
+ return undef;
+ }
+ $on =~ s/^$param\s+//; # remove the service name and spaces
+ $on =~ s/[0-6]:off\s*//g; # remove any runlevel:off entries
+ $on =~ s/:on\s*//g; # remove the :on from the runlevels
+ # what remains is a list of runlevels in which the service is on,
+ # or a null string if it is never turned on
+ chomp $on; # newline should be gone already (\s)
+ &B_log("DEBUG","chkconfig returned: $param=$on\n");
+
+ if ($on =~ /^\d+$/) {
+ # service is not off
+ ########################### BREAK out, don't skip question
+ return NOTSECURE_CAN_CHANGE();
+ }
+ }
+ elsif ($serviceType =~ /inet/) {
+ my $on = &B_Backtick("$chkconfig --list $param 2>&1");
+ if ($on =~ /^$param:\s+unknown/) {
+ # This service isn't installed on the system
+ return NOT_INSTALLED();
+ }
+ if ($on =~ /^error reading information on service $param: No such file or directory/) {
+ # This service isn't installed on the system
+ return NOT_INSTALLED();
+ }
+ if ($on =~ /^error/ ) {
+ # Something else is wrong?
+ # return undef
+ return undef;
+ }
+ if ($on =~ tr/\n// > 1) {
+ $on =~ s/^xinetd.+\n//;
+ }
+ $on =~ s/^\s*$param:?\s+//; # remove the service name and spaces
+ chomp $on; # newline should be gone already (\s)
+ &B_log("DEBUG","chkconfig returned: $param=$on\n");
+
+ if ($on =~ /^on$/) {
+ # service is not off
+ ########################### BREAK out, don't skip question
+ return NOTSECURE_CAN_CHANGE();
+ }
+ }
+ else {
+ # perhaps the service is started by inittab
+ my $inittabline = &B_Backtick("$grep -E '^[^#].{0,3}:.*:.+:.*$param' $inittab");
+ if ($inittabline =~ /.+/) { # . matches anything except newlines
+ # service is not off
+ &B_log("DEBUG","Checking inittab; found $inittabline\n");
+ ########################### BREAK out, don't skip question
+ return NOTSECURE_CAN_CHANGE();
+ }
+ }
+ } # foreach my $param
+
+
+ # boot-time parameters are not set; check processes
+ # Note the checkProcsforService returns INCONSISTENT() if a process is found
+ # assuming the checks above
+ return &checkProcsForService($service);
+}
+
+1;
+
+
diff --git a/recipes-security/bastille/files/config b/recipes-security/bastille/files/config
new file mode 100755
index 0000000..9e5e206
--- /dev/null
+++ b/recipes-security/bastille/files/config
@@ -0,0 +1,106 @@
+# Q: Would you like to enforce password aging? [Y]
+AccountSecurity.passwdage="Y"
+# Q: Should Bastille disable clear-text r-protocols that use IP-based authentication? [Y]
+AccountSecurity.protectrhost="Y"
+# Q: Should we disallow root login on tty's 1-6? [N]
+AccountSecurity.rootttylogins="Y"
+# Q: What umask would you like to set for users on the system? [077]
+AccountSecurity.umask="077"
+# Q: Do you want to set the default umask? [Y]
+AccountSecurity.umaskyn="Y"
+# Q: Would you like to deactivate the Apache web server? [Y]
+Apache.apacheoff="Y"
+# Q: Would you like to password protect single-user mode? [Y]
+BootSecurity.passsum="Y"
+# Q: Should we restrict console access to a small group of user accounts? [N]
+ConfigureMiscPAM.consolelogin="Y"
+# Q: Which accounts should be able to login at console? [root]
+ConfigureMiscPAM.consolelogin_accounts="root"
+# Q: Would you like to put limits on system resource usage? [N]
+ConfigureMiscPAM.limitsconf="Y"
+# Q: Would you like to set more restrictive permissions on the administration utilities? [N]
+FilePermissions.generalperms_1_1="Y"
+# Q: Would you like to disable SUID status for mount/umount?
+FilePermissions.suidmount="Y"
+# Q: Would you like to disable SUID status for ping? [Y]
+FilePermissions.suidping="Y"
+# Q: Would you like to disable SUID status for traceroute? [Y]
+FilePermissions.suidtrace="Y"
+# Q: Do you need the advanced networking options?
+Firewall.ip_advnetwork="Y"
+# Q: Should Bastille run the firewall and enable it at boot time? [N]
+Firewall.ip_enable_firewall="Y"
+# Q: Would you like to run the packet filtering script? [N]
+Firewall.ip_intro="Y"
+# Q: Interfaces for DHCP queries: [ ]
+Firewall.ip_s_dhcpiface=" "
+# Q: DNS servers: [0.0.0.0/0]
+Firewall.ip_s_dns="10.184.9.1"
+# Q: ICMP allowed types: [destination-unreachable echo-reply time-exceeded]
+Firewall.ip_s_icmpallowed="destination-unreachable echo-reply time-exceeded"
+# Q: ICMP services to audit: [ ]
+Firewall.ip_s_icmpaudit=" "
+# Q: ICMP types to disallow outbound: [destination-unreachable time-exceeded]
+Firewall.ip_s_icmpout="destination-unreachable time-exceeded"
+# Q: Internal interfaces: [ ]
+Firewall.ip_s_internaliface=" "
+# Q: TCP service names or port numbers to allow on private interfaces: [ ]
+Firewall.ip_s_internaltcp=" "
+# Q: UDP service names or port numbers to allow on private interfaces: [ ]
+Firewall.ip_s_internaludp=" "
+# Q: Masqueraded networks: [ ]
+Firewall.ip_s_ipmasq=" "
+# Q: Kernel modules to masquerade: [ftp raudio vdolive]
+Firewall.ip_s_kernelmasq="ftp raudio vdolive"
+# Q: NTP servers to query: [ ]
+Firewall.ip_s_ntpsrv=" "
+# Q: Force passive mode? [N]
+Firewall.ip_s_passiveftp="N"
+# Q: Public interfaces: [eth+ ppp+ slip+]
+Firewall.ip_s_publiciface="eth+ ppp+ slip+"
+# Q: TCP service names or port numbers to allow on public interfaces:[ ]
+Firewall.ip_s_publictcp=" "
+# Q: UDP service names or port numbers to allow on public interfaces:[ ]
+Firewall.ip_s_publicudp=" "
+# Q: Reject method: [DENY]
+Firewall.ip_s_rejectmethod="DENY"
+# Q: Enable source address verification? [Y]
+Firewall.ip_s_srcaddr="Y"
+# Q: TCP services to audit: [telnet ftp imap pop3 finger sunrpc exec login linuxconf ssh]
+Firewall.ip_s_tcpaudit="telnet ftp imap pop3 finger sunrpc exec login linuxconf ssh"
+# Q: TCP services to block: [2049 2065:2090 6000:6020 7100]
+Firewall.ip_s_tcpblock="2049 2065:2090 6000:6020 7100"
+# Q: Trusted interface names: [lo]
+Firewall.ip_s_trustiface="lo"
+# Q: UDP services to audit: [31337]
+Firewall.ip_s_udpaudit="31337"
+# Q: UDP services to block: [2049 6770]
+Firewall.ip_s_udpblock="2049 6770"
+# Q: Would you like to add additional logging? [Y]
+Logging.morelogging="Y"
+# Q: Would you like to set up process accounting? [N]
+Logging.pacct="N"
+# Q: Do you have a remote logging host? [N]
+Logging.remotelog="N"
+# Q: Would you like to disable acpid and/or apmd? [Y]
+MiscellaneousDaemons.apmd="Y"
+# Q: Would you like to deactivate NFS and Samba? [Y]
+MiscellaneousDaemons.remotefs="Y"
+# Q: Would you like to disable printing? [N]
+Printing.printing="Y"
+# Q: Would you like to disable printing? [N]
+Printing.printing_cups="Y"
+# Q: Would you like to display "Authorized Use" messages at log-in time? [Y]
+SecureInetd.banners="Y"
+# Q: Should Bastille ensure inetd's FTP service does not run on this system? [y]
+SecureInetd.deactivate_ftp="Y"
+# Q: Should Bastille ensure the telnet service does not run on this system? [y]
+SecureInetd.deactivate_telnet="Y"
+# Q: Who is responsible for granting authorization to use this machine?
+SecureInetd.owner="its owner"
+# Q: Would you like to set a default-deny on TCP Wrappers and xinetd? [N]
+SecureInetd.tcpd_default_deny="Y"
+# Q: Do you want to stop sendmail from running in daemon mode? [Y]
+Sendmail.sendmaildaemon="Y"
+# Q: Would you like to install TMPDIR/TMP scripts? [N]
+TMPDIR.tmpdir="N"
diff --git a/recipes-security/bastille/files/fix_version_parse.patch b/recipes-security/bastille/files/fix_version_parse.patch
new file mode 100644
index 0000000..3163af1
--- /dev/null
+++ b/recipes-security/bastille/files/fix_version_parse.patch
@@ -0,0 +1,21 @@
+Index: Bastille/bin/bastille
+===================================================================
+--- Bastille.orig/bin/bastille
++++ Bastille/bin/bastille
+@@ -162,11 +162,12 @@ fi
+ # We check that the version is at least the minimum
+
+ PERL_VERSION=`${CURRENT_PERL_PATH}/perl -version |
+- head -2 | # the second line contains the version
++ head -n 2 | # the second line contains the version
+ tr " " "\n" | # split words into separate lines
+- sed -e "s/^v//" | # to get rid of the v in v5.6.0
+- grep "^[1-9]\." | # find a "word" that starts with number dot
+- sed -e "s/_/./"` # substitute _patchlevel with .patchlevel
++ grep "^(v" | # find a "word" that starts with '(v'
++ sed -e "s/^(v//" -e "s/)//" -e "s/_/./"`
++ # to get rid of the (v in v5.6.0
++ # substitute _patchlevel with .patchlevel
+ # (used in 5.005_03 and prior)
+
+ # everything before the first .
diff --git a/recipes-security/bastille/files/yocto-standard-patch.patch b/recipes-security/bastille/files/yocto-standard-patch.patch
new file mode 100644
index 0000000..4f78a3b
--- /dev/null
+++ b/recipes-security/bastille/files/yocto-standard-patch.patch
@@ -0,0 +1,72 @@
+From c59b84ca3bda8e4244d47901b6966f28dd675434 Mon Sep 17 00:00:00 2001
+From: Andrei Dinu <andrei.adrianx.dinu@intel.com>
+Date: Thu, 23 May 2013 15:12:23 +0300
+Subject: [PATCH] added yocto-standard to bastille
+
+In order to make Bastille functional and avoid errors
+regarding distros, if not any given distro is identified,
+yocto-standard distro is added to the distro variable
+in Bastille.
+
+Fixed also some warnings regarding defined statements
+in API.pm.
+
+Signed-off-by: Andrei Dinu <andrei.adrianx.dinu@intel.com>
+---
+ Bastille/API.pm | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/Bastille/API.pm b/Bastille/API.pm
+index 40f8c72..ebbe9f7 100644
+--- a/Bastille/API.pm
++++ b/Bastille/API.pm
+@@ -445,8 +445,8 @@ sub GetDistro() {
+ $release=`/usr/bin/uname -sr`;
+ }
+ else {
+- print STDERR "$err Could not determine operating system version!\n";
+- $distro="unknown";
++ #print STDERR "$err Could not determine operating system version!\n";
++ $distro="3.8.11-yocto-standard";
+ }
+
+ # Figure out what kind of system we're on.
+@@ -537,7 +537,7 @@ sub getSupportedOSHash () {
+ "DB2.2", "DB3.0",
+ "RH6.0","RH6.1","RH6.2","RH7.0",
+ "RH7.1","RH7.2","RH7.3","RH8.0",
+- "RH9",
++ "RH9","3.8.11-yocto-standard",
+ "RHEL5",
+ "RHEL4AS","RHEL4ES","RHEL4WS",
+ "RHEL3AS","RHEL3ES","RHEL3WS",
+@@ -1284,7 +1284,7 @@ sub B_write_sums {
+
+ my $sumFile = &getGlobal('BFILE',"sum.csv");
+
+- if ( defined %GLOBAL_SUM ) {
++ if ( %GLOBAL_SUM ) {
+
+ open( SUM, "> $sumFile") or &B_log("ERROR","Unable to open $sumFile for write.\n$!\n");
+
+@@ -1318,7 +1318,7 @@ sub B_check_sum($) {
+ my $file = $_[0];
+ my $cksum = &getGlobal('BIN',"cksum");
+
+- if (not(defined(%GLOBAL_SUM))) {
++ if (not(%GLOBAL_SUM)) {
+ &B_read_sums;
+ }
+
+@@ -1375,7 +1375,7 @@ sub listModifiedFiles {
+ sub B_isFileinSumDB($) {
+ my $file = $_[0];
+
+- if (not(defined(%GLOBAL_SUM))) {
++ if (not(%GLOBAL_SUM)) {
+ &B_log("DEBUG","Reading in DB from B_isFileinSumDB");
+ &B_read_sums;
+ }
+--
+1.7.9.5
+
diff --git a/recipes-security/checksecurity/checksecurity_2.0.14.bb b/recipes-security/checksecurity/checksecurity_2.0.14.bb
new file mode 100644
index 0000000..951a3e6
--- /dev/null
+++ b/recipes-security/checksecurity/checksecurity_2.0.14.bb
@@ -0,0 +1,16 @@
+DESCRIPTION = "basic system security checks"
+SECTION = "security"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
+
+SRC_URI = "http://ftp.de.debian.org/debian/pool/main/c/checksecurity/checksecurity_${PV}.tar.gz"
+
+SRC_URI[md5sum] = "ad6cfe0cd66ebdd16dd5d4ee5fa8fa17"
+SRC_URI[sha256sum] = "a2bc2355358d6daf3cb72485d564e82cb541e8516f23b50522c816853ecd13c2"
+
+do_compile() {
+}
+
+do_install() {
+ oe_runmake PREFIX=${D}
+}
diff --git a/recipes-security/curses-perl/curses-perl_1.28.bb b/recipes-security/curses-perl/curses-perl_1.28.bb
new file mode 100644
index 0000000..5984ca7
--- /dev/null
+++ b/recipes-security/curses-perl/curses-perl_1.28.bb
@@ -0,0 +1,27 @@
+DESCRIPTION = "This package contains the URI.pm module with friends. \
+The module implements the URI class. URI objects can be used to access \
+and manipulate the various components that make up these strings."
+
+SECTION = "libs"
+LICENSE = "Artistic-1.0 | GPL-1.0+"
+PR = "r0"
+
+LIC_FILES_CHKSUM = "file://README;beginline=26;endline=30;md5=0b37356c5e9e28080a3422d82af8af09"
+
+DEPENDS += "perl ncurses"
+
+SRC_URI = "http://www.cpan.org/authors/id/G/GI/GIRAFFED/Curses-1.28.tgz"
+
+SRC_URI[md5sum] = "ed9f7ddf2d90f4266da91c3dc9fad9c9"
+SRC_URI[sha256sum] = "613b73c4b6075b1550592812214e4fc0e2205d3afcf234e3fa90f208fb8de892"
+
+S = "${WORKDIR}/Curses-${PV}"
+
+EXTRA_CPANFLAGS = "EXPATLIBPATH=${STAGING_LIBDIR} EXPATINCPATH=${STAGING_INCDIR}"
+
+inherit cpan
+
+do_compile() {
+ export LIBC="$(find ${STAGING_DIR_TARGET}/${base_libdir}/ -name 'libc-*.so')"
+ cpan_do_compile
+}
diff --git a/recipes-security/lib-perl/lib-perl_0.63.bb b/recipes-security/lib-perl/lib-perl_0.63.bb
new file mode 100644
index 0000000..606ecfb
--- /dev/null
+++ b/recipes-security/lib-perl/lib-perl_0.63.bb
@@ -0,0 +1,27 @@
+DESCRIPTION = "This package contains the URI.pm module with friends. \
+The module implements the URI class. URI objects can be used to access \
+and manipulate the various components that make up these strings."
+
+SECTION = "libs"
+LICENSE = "Artistic-1.0 | GPL-1.0+"
+PR = "r0"
+
+LIC_FILES_CHKSUM = "file://README;beginline=26;endline=30;md5=94b119f1a7b8d611efc89b5d562a1a50"
+
+DEPENDS += "perl"
+
+SRC_URI = "http://www.cpan.org/authors/id/S/SM/SMUELLER/lib-${PV}.tar.gz"
+
+SRC_URI[md5sum] = "8607ac4e0d9d43585ec28312f52df67c"
+SRC_URI[sha256sum] = "72f63db9220098e834d7a38231626bd0c9b802c1ec54a628e2df35f3818e5a00"
+
+S = "${WORKDIR}/lib-${PV}"
+
+EXTRA_CPANFLAGS = "EXPATLIBPATH=${STAGING_LIBDIR} EXPATINCPATH=${STAGING_INCDIR}"
+
+inherit cpan
+
+do_compile() {
+ export LIBC="$(find ${STAGING_DIR_TARGET}/${base_libdir}/ -name 'libc-*.so')"
+ cpan_do_compile
+}
diff --git a/recipes-security/pax-utils/pax-utils_0.7.bb b/recipes-security/pax-utils/pax-utils_0.7.bb
new file mode 100644
index 0000000..63cf0ea
--- /dev/null
+++ b/recipes-security/pax-utils/pax-utils_0.7.bb
@@ -0,0 +1,20 @@
+SUMMARY = "Security-focused ELF files checking tool"
+DESCRIPTION = "This is a small set of various PaX aware and related \
+utilities for ELF binaries. It can check ELF binary files and running \
+processes for issues that might be relevant when using ELF binaries \
+along with PaX, such as non-PIC code or executable stack and heap."
+HOMEPAGE = "http://www.gentoo.org/proj/en/hardened/pax-utils.xml"
+LICENSE = "GPLv2+"
+LIC_FILES_CHKSUM = "file://COPYING;md5=eb723b61539feef013de476e68b5c50a"
+
+SRC_URI = "http://gentoo.osuosl.org/distfiles/pax-utils-${PV}.tar.xz"
+
+SRC_URI[md5sum] = "8ae7743ad11500f7604f2e817221d877"
+SRC_URI[sha256sum] = "1ac4cee9a9ca97a723505eb29a25e50adeccffba3f0f0ef4f035cf082caf3b84"
+PR = "r0"
+
+do_install() {
+ oe_runmake PREFIX=${D}${prefix} DESTDIR=${D} install
+}
+
+BBCLASSEXTEND = "native"
diff --git a/recipes-security/redhat-security/files/find-chroot-py.sh b/recipes-security/redhat-security/files/find-chroot-py.sh
new file mode 100644
index 0000000..9996e08
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-chroot-py.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# find-chroot-py utility
+# Copyright (c) 2011 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# This program looks for python apps that use chroot(2) without using chdir(2)
+#
+# To save to file: ./find-chroot | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee findings.txt
+
+libdirs="/lib /lib64 /usr/lib /usr/lib64"
+progdirs="/bin /sbin /usr/bin /usr/sbin /usr/libexec"
+FOUND=0
+
+# First param is which list to use, second is search pattern
+scan () {
+if [ "$1" = "1" ] ; then
+ dirs=$libdirs
+elif [ "$1" = "2" ] ; then
+ dirs=$progdirs
+elif [ "$1" = "3" ] ; then
+ dirs=$3
+fi
+
+for d in $dirs ; do
+ if [ ! -d $d ] ; then
+ continue
+ fi
+ files=`/usr/bin/find $d -name "$2" -type f 2>/dev/null`
+ for f in $files
+ do
+ if [ "$1" = "2" ] ; then
+ testf=`/usr/bin/file $f | egrep 'ython'`
+ if [ x"$testf" = "x" ] ; then
+ continue
+ fi
+ fi
+ syms=`egrep ' os.chroot' $f`
+ if [ x"$syms" != "x" ] ; then
+ syms=`egrep ' os.chdir' $f`
+ if [ x"$syms" = "x" ] ; then
+ if [ $FOUND = 0 ] ; then
+ printf "%-44s%s\n" "FILE" " PACKAGE"
+ FOUND=1
+ fi
+ # Red
+ printf "\033[31m%-44s\033[m" $f
+ #rpm -qf --queryformat "%{NAME}-%{VERSION}" $f
+ rpm -qf --queryformat " %{SOURCERPM}" $f
+ echo
+ else
+ # One last test to see if chdir is within 4
+ # lines of chroot
+ syms=`cat $f | egrep ' os.chroot' -A3 | egrep ' os.chdir'`
+ if [ x"$syms" = "x" ] ; then
+ if [ $FOUND = 0 ] ; then
+ printf "%-44s%s\n" "FILE" " PACKAGE"
+ FOUND=1
+ fi
+ printf "\033[31m%-44s\033[m" $f
+ rpm -qf --queryformat " %{SOURCERPM}" $f
+ echo
+ fi
+ fi
+ fi
+ done
+done
+}
+
+if [ $# -eq 1 ] ; then
+ if [ -d $1 ] ; then
+ scan 3 '*' $1
+ else
+ echo "Input is not a directory"
+ exit 1
+ fi
+else
+ scan 2 '*'
+ scan 1 '*.py'
+fi
+
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+ echo "No problems found" 1>&2
+ exit 0
+fi
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/find-chroot.sh b/recipes-security/redhat-security/files/find-chroot.sh
new file mode 100644
index 0000000..adce7fc
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-chroot.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# find-chroot utility
+# Copyright (c) 2011 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# This program looks for apps that use chroot(2) without using chdir(2)
+#
+# To save to file: ./find-chroot | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee findings.txt
+
+libdirs="/lib /lib64 /usr/lib /usr/lib64"
+progdirs="/bin /sbin /usr/bin /usr/sbin /usr/libexec"
+FOUND=0
+
+# First param is which list to use, second is search pattern
+scan () {
+if [ "$1" = "1" ] ; then
+ dirs=$libdirs
+elif [ "$1" = "2" ] ; then
+ dirs=$progdirs
+elif [ "$1" = "3" ] ; then
+ dirs=$3
+fi
+
+for d in $dirs ; do
+ if [ ! -d $d ] ; then
+ continue
+ fi
+ files=`/usr/bin/find $d -name "$2" -type f 2>/dev/null`
+ for f in $files
+ do
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' chroot@.*GLIBC'`
+ if [ x"$syms" != "x" ] ; then
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' chdir@.*GLIBC'`
+ if [ x"$syms" = "x" ] ; then
+ if [ $FOUND = 0 ] ; then
+ printf "%-44s%s\n" "FILE" " PACKAGE"
+ FOUND=1
+ fi
+ # Red
+ printf "\033[31m%-44s\033[m" $f
+ #rpm -qf --queryformat "%{NAME}-%{VERSION}" $f
+ rpm -qf --queryformat " %{SOURCERPM}" $f
+ echo
+ else
+ # One last test to see if chdir is within 3
+ # lines of chroot
+ syms=`objdump -d $f | egrep callq | egrep 'chroot@plt' -A2 | egrep 'chroot|chdir'`
+ if [ x"$syms" = "x" ] ; then
+ syms=`echo $f | egrep -v 'libc-2|libc.so'`
+ if [ x"$syms" != "x" ] ; then
+ if [ $FOUND = 0 ] ; then
+ printf "%-44s%s\n" "FILE" "PACKAGE"
+ FOUND=1
+ fi
+ printf "\033[31m%-44s\033[m" $f
+ rpm -qf --queryformat " %{SOURCERPM}" $f
+ echo
+ fi
+ fi
+ fi
+ fi
+ done
+done
+}
+
+if [ $# -eq 1 ] ; then
+ if [ -d $1 ] ; then
+ scan 3 '*' $1
+ else
+ echo "Input is not a directory"
+ exit 1
+ fi
+else
+ scan 2 '*'
+ scan 1 '*.so'
+fi
+
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+ echo "No problems found" 1>&2
+ exit 0
+fi
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/find-elf4tmp.sh b/recipes-security/redhat-security/files/find-elf4tmp.sh
new file mode 100644
index 0000000..8f39baa
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-elf4tmp.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+# find_elf4tmp utility
+# Copyright (c) 2010-12 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script will search a directory and its subdirectories for all elf
+# executables. It will then search for the use of the tmp directory. If it finds
+# this is true, it will then check to see if XXX is being used which would
+# indicate that the path is going to be randomized.
+
+if [ $# -ge 2 ] ; then
+ echo "Usage: find_elf4tmp [directory]" 1>&2
+ exit 1
+fi
+if [ ! -x /usr/bin/eu-strings ] ; then
+ echo "Skipping due to missing /usr/bin/eu-strings utility"
+ exit 1
+fi
+if [ -h /bin ] ; then
+ DIRS="/usr/bin /usr/sbin /usr/libexec /usr/kerberos /usr/games /usr/lib /usr/lib64 /usr/local"
+else
+ DIRS="/bin /sbin /usr/bin /usr/sbin /usr/libexec /usr/kerberos /usr/games /lib /lib64 /usr/lib /usr/lib64 /usr/local"
+fi
+if [ $# -eq 1 ] ; then
+ if [ -d "$1" ] ; then
+ DIRS="$1"
+ else
+ echo "Option passed in was not a directory" 1>&2
+ exit 1
+ fi
+fi
+
+FOUND=0
+for d in $DIRS
+do
+ if [ ! -d $d ] ; then
+ continue
+ fi
+# echo "Scanning files in $d..."
+ for f in `/usr/bin/find $d -type f 2>/dev/null`
+ do
+ # Get just the elf executables
+ testf=`echo $f | /usr/bin/file -n -f - 2>/dev/null | grep ELF`
+ if [ x"$testf" != "x" ] ; then
+ test_res=`/usr/bin/eu-strings $f | /bin/grep '/tmp/' | /bin/egrep -v 'XX|/tmp/$|[ .,:]/tmp/'`
+ if [ x"$test_res" = "x" ] ; then
+ continue
+ fi
+
+ # Do further examination...
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' mkstemp@.*GLIBC| tempnam@.*GLIBC| tmpfile@.*GLIBC'`
+ if [ x"$syms" != "x" ] ; then
+ continue
+ fi
+
+ # Well its a bad one...out with it
+ FOUND=1
+
+ # Get the package
+ RPM=`/bin/rpm -qf --queryformat "%{NAME}-%{VERSION}" $f 2>/dev/null | /bin/grep -v 'not owned' | /bin/sort | /usr/bin/uniq`
+ if [ x"$RPM" = "x" ] ; then
+ RPM="<unowned>"
+ fi
+
+ # For each tmp string, output the line
+ echo $test_res | /usr/bin/tr '\b' '\n' | /bin/awk 'NF >= 1 { printf "%-46s\t%-30s\t%s\n", f, r, $1 }' r=$RPM f=$f
+ fi
+ done
+done
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+ echo "No problems found" 1>&2
+ exit 0
+fi
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/find-execstack.sh b/recipes-security/redhat-security/files/find-execstack.sh
new file mode 100644
index 0000000..85f16de
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-execstack.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# find-execstack utility
+# Copyright (c) 2007 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# This program looks for executable stacks
+#
+
+libdirs="/lib /lib64 /usr/lib /usr/lib64"
+progdirs="/bin /sbin /usr/bin /usr/sbin /usr/libexec"
+FOUND=0
+
+# First param is which list to use, second is search pattern
+scan () {
+if [ "$1" = "1" ] ; then
+ dirs=$libdirs
+elif [ "$1" = "2" ] ; then
+ dirs=$progdirs
+fi
+
+for d in $dirs ; do
+ if [ ! -d $d ] ; then
+ continue
+ fi
+ files=`/usr/bin/find $d -name "$2" -type f 2>/dev/null`
+ for f in $files
+ do
+ FOUND_ONE=0
+ stacks=`/usr/bin/eu-readelf -l $f 2>/dev/null | grep STACK`
+ if [ x"$stacks" != "x" ] ; then
+ perms=`echo $stacks | /bin/awk '{ print $7 }'`
+ if [ x"$perms" != x -a "$perms" != "RW" ] ; then
+ FOUND_ONE=1
+ fi
+ fi
+ old_stacks=`echo $stacks | /bin/grep -v GNU_STACK`
+ if [ x"$old_stacks" != "x" ] ; then
+ FOUND_ONE=1
+ fi
+ heaps=`/usr/bin/eu-readelf -l $f 2>/dev/null | grep GNU_HEAP`
+ if [ x"$heaps" != "x" ] ; then
+ FOUND_ONE=1
+ fi
+ if [ $FOUND_ONE = 1 ] ; then
+ printf "%-42s" $f
+ rpm -qf --queryformat "%{SOURCERPM}" $f
+ echo
+ FOUND=1
+ fi
+ done
+done
+}
+
+scan 1 '*.so'
+scan 2 '*'
+
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+ echo "No problems found" 1>&2
+ exit 0
+fi
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/find-hidden-exec.sh b/recipes-security/redhat-security/files/find-hidden-exec.sh
new file mode 100644
index 0000000..f799fca
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-hidden-exec.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# This program looks for hidden executables
+
+find / -name '.*' -type f -perm /00111 2>/dev/null
+
+# Also need to find hidden dirs and see if anything below it is hidden
+hidden_dirs=`find / -name '.*' -type d 2>/dev/null`
+for d in $hidden_dirs
+do
+ find $d -name '.*' -type f -perm /00111 2>/dev/null
+done
+
diff --git a/recipes-security/redhat-security/files/find-nodrop-groups.sh b/recipes-security/redhat-security/files/find-nodrop-groups.sh
new file mode 100644
index 0000000..249eacd
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-nodrop-groups.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# find-nodrop-groups utility
+# Copyright (c) 2011 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# This program looks for apps that use setgid(2) without using initgroups(3)
+# or setgroups(2).
+#
+# To save to file: ./find-nodrop-groups | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee findings.txt
+
+libdirs="/lib /lib64 /usr/lib /usr/lib64"
+progdirs="/bin /sbin /usr/bin /usr/sbin /usr/libexec"
+FOUND=0
+
+# First param is which list to use, second is search pattern
+scan () {
+if [ "$1" = "1" ] ; then
+ dirs=$libdirs
+elif [ "$1" = "2" ] ; then
+ dirs=$progdirs
+elif [ "$1" = "3" ] ; then
+ dirs=$3
+fi
+
+for d in $dirs ; do
+ if [ ! -d $d ] ; then
+ continue
+ fi
+ files=`/usr/bin/find $d -name "$2" -type f 2>/dev/null`
+ for f in $files
+ do
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' setgid@.*GLIBC| setegid@.*GLIBC| setresgid@.*GLIBC'`
+ if [ x"$syms" != "x" ] ; then
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' setuid@.*GLIBC| seteuid@.*GLIBC| setresuid@.*GLIBC'`
+ if [ x"$syms" != "x" ] ; then
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' setgroups@.*GLIBC| initgroups@.*GLIBC'`
+ if [ x"$syms" = "x" ] ; then
+ if [ $FOUND = 0 ] ; then
+ printf "%-44s%s\n" "FILE" "PACKAGE"
+ fi
+ syms=`find $f \( -perm -004000 -o -perm -002000 \) -type f -print`
+ if [ x"$syms" = "x" ] ; then
+ printf "\033[31m%-44s\033[m" $f
+ rpm -qf --queryformat "%{SOURCERPM}" $f
+ echo
+ FOUND=1
+# else
+# printf "\033[33m%-44s\033[m" $f
+ fi
+ #rpm -qf --queryformat "%{NAME}-%{VERSION}" $f
+ fi
+ fi
+ fi
+ done
+done
+}
+
+if [ $# -eq 1 ] ; then
+ if [ -d $1 ] ; then
+ scan 3 '*' $1
+ else
+ echo "Input is not a directory"
+ exit 1
+ fi
+else
+ scan 1 '*.so'
+ scan 2 '*'
+fi
+
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+ echo "No problems found" 1>&2
+ exit 0
+fi
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/find-sh4errors.sh b/recipes-security/redhat-security/files/find-sh4errors.sh
new file mode 100644
index 0000000..0054a6a
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-sh4errors.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+# find_sh4errors utility
+# Copyright (c) 2004 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script will search a directory and its subdirectories for every shell
+# script. It then runs sh -n to see if bash can determine if there are obvious
+# parsing errors. It does have a bug in that bash -n does not take into
+# account someone may program an unconditional exit and then include man page
+# generation information. It also fails to notice the exec command. When you
+# run across files that do either of the above, add it to the KNOWN_BAD list.
+
+if [ $# -ge 2 ] ; then
+ echo "Usage: find_sh4errors [directory]" 1>&2
+ exit 1
+fi
+INTERPRETERS="wish wishx tclsh guile rep itkwish expect /etc/kde/kdm/Xsession /etc/X11/xdm/Xsession /usr/bin/festival perl hfssh"
+SKIP_DIRS="/opt /home /root"
+KNOWN_BAD="/usr/bin/kde-build /usr/bin/cvsversion samples/copifuncs/copi.sendifm1 bashdb bash_completion_test"
+DIR="/"
+if [ $# -eq 1 ] ; then
+ if [ -d "$1" ] ; then
+ DIR="$1"
+ else
+ echo "Option passed in was not a directory" 1>&2
+ exit 1
+ fi
+fi
+tempfile=`mktemp /tmp/sh4.XXXXXX`
+tempfile2=`mktemp /tmp/sh4.XXXXXX`
+if [ -z "$tempfile" -o -z "$tempfile2" ] ; then
+ echo ; echo "Unable to create tempfiles...aborting." 1>&2 ; echo
+ exit 1
+fi
+trap "rm -f $tempfile; rm -f $tempfile2; exit 2" 1 2 3 5 15
+
+# Get executable files
+#echo "Locating executables..."
+/usr/bin/find $DIR -type f -perm /0111 -print >> $tempfile 2>/dev/null
+FOUND=0
+#echo "Refining list to shell scripts..."
+while read f
+do
+ # Get just the shell scripts
+ testf=`echo $f | /usr/bin/file -n -f - | egrep 'ourne|POSIX shell'`
+ if [ x"$testf" != x ] ; then
+ echo $f >> $tempfile2
+ FOUND=1
+ fi
+done < $tempfile
+/bin/rm -f $tempfile
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+# echo "Examining shell scripts in $DIR"
+# echo "No problems found"
+ /bin/rm -f $tempfile2
+ exit 0
+fi
+#echo "Examining shell scripts in $DIR"
+FOUND=0
+while read i
+do
+ # First see if the script calls an interpreter
+ SKIP=0
+ for lang in $INTERPRETERS
+ do
+ if `/bin/cat "$i" 2>/dev/null | \
+ grep "exec[ \t].*$lang" >/dev/null` ; then
+ SKIP=1
+ break
+ fi
+ done
+
+ if [ $SKIP -eq 1 ] ; then
+ continue
+ fi
+
+ # See if this is in a dir we want to ignore
+ for d in $SKIP_DIRS
+ do
+ if `echo "$i" | /bin/grep "^\$d" >/dev/null`; then
+ SKIP=1
+ break
+ fi
+ done
+
+ if [ $SKIP -eq 1 ] ; then
+ continue
+ fi
+
+ # Don't do the known naughty files
+ for bad in $KNOWN_BAD
+ do
+ if `echo "$i" | /bin/grep "$bad" >/dev/null`; then
+ SKIP=1
+ break
+ fi
+ done
+
+ if [ $SKIP -eq 1 ] ; then
+ continue
+ fi
+
+ # Now examine them for correctness
+ interp=`/usr/bin/head -n 1 "$i" | /bin/awk '{ print $1 }' | \
+ /usr/bin/tr -d '#!'`
+ if [ x"$interp" = "x" -o ! -x "$interp" ] ; then
+ interp="/bin/sh"
+ fi
+ $interp -n "$i" 2>/dev/null
+ if [ $? -ne 0 ] ; then
+ printf "%-44s" "$i"
+ rpm -qf --queryformat "%{NAME}-%{VERSION}" $i
+ echo
+ FOUND=1
+ fi
+done < $tempfile2
+/bin/rm -f $tempfile2
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+# echo "No problems found"
+ exit 0
+fi
+exit 1
+
diff --git a/recipes-security/redhat-security/files/find-sh4tmp.sh b/recipes-security/redhat-security/files/find-sh4tmp.sh
new file mode 100644
index 0000000..7fd1af7
--- /dev/null
+++ b/recipes-security/redhat-security/files/find-sh4tmp.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+# find_sh4tmp utility
+# Copyright (c) 2005 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This script will search a directory and its subdirectories for all shell
+# scripts. It will then search for the use of the tmp directory. If it finds
+# this is true, it will then try to determine if mktemp or something
+# reasonable was used and exclude it. It has a bug in that it does not handle
+# rm -f /tmp/ or mkdir /tmp/ correctly. If you run across files that do that,
+# add them to the KNOWN_BAD list to ignore them.
+
+if [ $# -ge 2 ] ; then
+ echo "Usage: find_sh4tmp [directory]" 1>&2
+ exit 1
+fi
+INTERPRETERS="wish wishx tclsh guile rep itkwish expect /etc/kde/kdm/Xsession /etc/X11/xdm/Xsession /usr/bin/festival perl hfssh"
+SKIP_DIRS="/opt /home /root /mnt /media /dev /proc /selinux /sys /usr/share/doc"
+KNOWN_BAD="kopete_latexconvert.sh cvs2dist fixfiles mysqlbug build/scripts/package/mkspec py-compile rc.sysinit init.d/xfs diff-jars grub-install mailshar vncserver Xsession sysreport cross-build vpkg rcs-to-cvs debug_check_log cvs2vendor tmpwatch ps2epsi mkdumprd xdg-open xdg-mime xdg-email gzexe"
+DIR="/"
+if [ $# -eq 1 ] ; then
+ if [ -d "$1" ] ; then
+ DIR="$1"
+ else
+ echo "Option passed in was not a directory" 1>&2
+ exit 1
+ fi
+fi
+tempfile=`mktemp /tmp/sh4.XXXXXX`
+tempfile2=`mktemp /tmp/sh4.XXXXXX`
+if [ -z "$tempfile" -o -z "$tempfile2" ] ; then
+ echo ; echo "Unable to create tempfiles...aborting." 1>&2 ; echo
+ exit 1
+fi
+trap "rm -f $tempfile; rm -f $tempfile2; exit 2" 1 2 3 5 15
+
+# Get executable files
+#echo "Scanning shell scripts in $DIR..."
+find $DIR -type f -perm /0111 -print >> $tempfile 2>/dev/null
+FOUND=0
+while read f
+do
+ # Get just the shell scripts
+ testf=`echo $f | file -n -f - | egrep 'ourne|POSIX shell'`
+ if [ x"$testf" != x ] ; then
+# FIXME: need to do something to get rid of echo, rm, or mkdir "/tmp/"
+ test_res=`cat $f 2>/dev/null | grep '\/tmp\/' | grep -v 'mktemp' | grep -v '^#'`
+ if [ x"$test_res" = x ] ; then
+ continue
+ fi
+
+ # Do further examination...
+ # First see if the script calls an interpreter
+ SKIP=0
+ for lang in $INTERPRETERS
+ do
+ if `cat "$f" | grep "exec[ \t].*$lang" >/dev/null` ; then
+ SKIP=1
+ break
+ fi
+ done
+
+ if [ $SKIP -eq 1 ] ; then
+ continue
+ fi
+
+ # See if this is in a dir we want to ignore
+ for d in $SKIP_DIRS
+ do
+ if `echo "$f" | grep "^\$d" >/dev/null`; then
+ SKIP=1
+ break
+ fi
+ done
+
+ if [ $SKIP -eq 1 ] ; then
+ continue
+ fi
+
+ # Don't do the known naughty files
+ for bad in $KNOWN_BAD
+ do
+ if `echo "$f" | grep "$bad" >/dev/null`; then
+ SKIP=1
+ break
+ fi
+ done
+
+ if [ $SKIP -eq 1 ] ; then
+ continue
+ fi
+
+ # Well its a bad one...out with it
+ printf "%-44s" $f
+ rpm -qf --queryformat "%{NAME}-%{VERSION}" $f
+ echo
+ FOUND=1
+ fi
+done < $tempfile
+rm -f $tempfile
+if [ $FOUND -eq 0 ] ; then
+ # Nothing to report, just exit
+# echo "No problems found"
+ rm -f $tempfile2
+ exit 0
+fi
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/lib-bin-check.sh b/recipes-security/redhat-security/files/lib-bin-check.sh
new file mode 100644
index 0000000..1e2d930
--- /dev/null
+++ b/recipes-security/redhat-security/files/lib-bin-check.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+found=0
+list=`rpm -qa --queryformat "%{NAME}-%{VERSION}.%{ARCH}\n" | grep '^lib' | egrep -v '\-utils\-|\-bin\-|\-tools\-|\-client\-|libreoffice|\-plugin\-'`
+for p in $list
+do
+ bin=`rpm -ql $p | egrep '^/bin|^/sbin|^/usr/bin|^/usr/sbin' | grep -v '\-config'`
+ if [ "x$bin" != "x" ]; then
+ testf=`echo $bin | /usr/bin/file -n -f - 2>/dev/null | grep ELF`
+ if [ x"$testf" != "x" ] ; then
+ found=1
+ echo "$p could be split into a utils package"
+ fi
+ fi
+done
+
+if [ $found = 0 ]; then
+ echo "No problems found"
+ exit 0
+fi
+
+exit 1
+
+
diff --git a/recipes-security/redhat-security/files/rpm-chksec.sh b/recipes-security/redhat-security/files/rpm-chksec.sh
new file mode 100644
index 0000000..983c218
--- /dev/null
+++ b/recipes-security/redhat-security/files/rpm-chksec.sh
@@ -0,0 +1,279 @@
+#!/bin/sh
+# rpm-chksec
+#
+# Copyright (c) 2011-2013 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Given an rpm, it will look at each file to check that its compiled with
+# the intended flags to make it more secure. Things that are green are OK.
+# Anything in yellow could be better but is passable. Anything in red needs
+# attention.
+#
+# If the --all option is given, it will generate a list of rpms and then
+# summarize the rpm's state. For yes, then all files are in the expected
+# state. Just one file not compiled with the right flags can turn the
+# answer to no. Re-run passing that package (instead of --all) for the details.
+#
+# To save to file: ./rpm-chksec | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee output.txt
+
+VERSION="0.5.2"
+
+usage () {
+ echo "rpm-chksec [--version|--all|<rpmname>...]"
+ if [ ! -x /usr/bin/filecap ] ; then
+ echo "You need to install libcap-ng-utils to test capabilities"
+ fi
+ if [ $EUID != 0 ] ; then
+ echo "You might need to be root to read some files"
+ fi
+ exit 0
+}
+
+if [ "$1" = "--help" -o $# -eq 0 ] ; then
+ usage
+fi
+if [ "$1" = "--version" ] ; then
+ echo "rpm-chksec $VERSION"
+ exit 0
+fi
+if [ "$1" = "--all" ] ; then
+ MODE="all"
+else
+ MODE="single"
+fi
+
+do_one () {
+if ! rpm -q $1 >/dev/null 2>&1 ; then
+ if [ "$MODE" = "single" ] ; then
+ echo "$1 is not installed"
+ exit 1
+ else
+ echo "not installed"
+ return
+ fi
+fi
+files=`rpm -ql $1`
+
+# Look for daemons, need this for later...
+DAEMON=""
+for f in $files
+do
+ if [ ! -f "$f" ] ; then
+ continue
+ fi
+ if [ `echo "$f" | grep '\/etc\/rc.d\/init.d'` ] ; then
+ n=`basename "$f"`
+ t=`which "$n" 2>/dev/null`
+ if [ x"$t" != "x" ] ; then
+ DAEMON="$DAEMON $t"
+ continue
+ fi
+ t=`which "$n"d 2>/dev/null`
+ if [ x"$t" != "x" ] ; then
+ DAEMON="$DAEMON $t"
+ continue
+ fi
+ t=`cat "$f" 2>/dev/null | grep 'bin' | grep 'exit 5' | grep -v '\$'`
+ if [ x"$t" != "x" ] ; then
+ DAEMON="$DAEMON $t"
+ continue
+ fi
+ if [ "$MODE" = "single" ] ; then
+ echo "Can't find the executable in $f but daemon rules would apply"
+ fi
+ elif [ `echo "$f" | grep '\/lib\/systemd\/'` ] ; then
+ t=`cat "$f" | grep -i '^ExecStart=' | tr '=' ' ' | awk '{ print $2 }'`
+ if [ x"$t" != "x" ] ; then
+ DAEMON="$DAEMON $t"
+ continue
+ fi
+ fi
+done
+
+# Prevent garbled output when doing --all.
+skip_current=0
+
+for f in $files
+do
+ if [ ! -f "$f" ] ; then
+ continue
+ fi
+ # Some packages have files with ~ in them. This avoids it.
+ if ! echo "$f" | grep '^/' >/dev/null ; then
+ continue
+ fi
+ if [ ! -r "$f" ] && [ $EUID != 0 ] ; then
+ if [ $MODE = "single" ] ; then
+ echo "Please re-test $f as the root user"
+ else
+ # Don't print results.
+ skip_current=1
+ echo "Please re-test $1 as the root user"
+ fi
+ continue
+ fi
+ if ! file "$f" | grep -qw 'ELF'; then
+ continue
+ fi
+ RELRO="no"
+ if readelf -l "$f" 2>/dev/null | grep -q 'GNU_RELRO'; then
+ RELRO="partial"
+ fi
+ if readelf -d "$f" 2>/dev/null | grep -q 'BIND_NOW'; then
+ RELRO="full"
+ fi
+ PIE="no"
+ if readelf -h "$f" 2>/dev/null | grep -q 'Type:[[:space:]]*DYN'; then
+ PIE="DSO"
+ if readelf -d "$f" 2>/dev/null | grep -q '(DEBUG)'; then
+ PIE="yes"
+ fi
+ fi
+ APP=""
+ if [ x"$DAEMON" != "x" ] ; then
+ for d in $DAEMON
+ do
+ if [ "$f" = "$d" ] ; then
+ APP="daemon"
+ break
+ fi
+ done
+ fi
+ if [ x"$APP" = "x" ] ; then
+ # See if this is a library or a setuid app
+ if [ `echo "$f" | grep '\/lib' | grep '\.so'` ] ; then
+ APP="library"
+ elif [ `find "$f" -perm -004000 -type f -print` ] ; then
+ APP="setuid"
+ elif [ `find "$f" -perm -002000 -type f -print` ] ; then
+ APP="setgid"
+ elif [ -x /usr/bin/filecap ] && [ `filecap "$f" 2> /dev/null | wc -w` -gt 0 ] ; then
+ APP="setcap"
+ else
+ syms1=`/usr/bin/readelf -s "$f" 2>/dev/null | egrep ' connect@.*GLIBC| listen@.*GLIBC| accept@.*GLIBC|accept4@.*GLIBC'`
+ syms2=`/usr/bin/readelf -s "$f" 2>/dev/null | egrep ' getaddrinfo@.*GLIBC| getnameinfo@.*GLIBC| getservent@.*GLIBC| getservbyname@.*GLIBC| getservbyport@.*GLIBC|gethostbyname@.*GLIBC| gethostbyname2@.*GLIBC| gethostbyaddr@.*GLIBC| gethostbyaddr2@.*GLIBC'`
+ if [ x"$syms1" != "x" ] ; then
+ if [ x"$syms2" != "x" ] ; then
+ APP="network-ip"
+ else
+ APP="network-local"
+ fi
+ fi
+ fi
+ fi
+ if [ x"$APP" = "x" ] ; then
+ APP="exec"
+ fi
+
+ # OK, ready for the output
+ if [ "$MODE" = "single" ] ; then
+ printf "%-56s %-10s " "$f" $APP
+ if [ "$APP" = "daemon" -o "$APP" = "setuid" -o "$APP" = "setgid" -o "$APP" = "setcap" -o "$APP" = "network-ip" -o "$APP" = "network-local" ] ; then
+ if [ "$RELRO" = "full" ] ; then
+ printf "\033[32m%-7s\033[m " $RELRO
+ elif [ "$RELRO" = "partial" ] ; then
+ printf "\033[33m%-7s\033[m " $RELRO
+ else
+ printf "\033[31m%-7s\033[m " $RELRO
+ fi
+ if [ "$PIE" = "yes" ] ; then
+ printf "\033[32m%-4s\033[m" $PIE
+ else
+ printf "\033[31m%-4s\033[m" $PIE
+ fi
+ elif [ "$APP" = "library" ] ; then
+ if [ "$RELRO" = "full" -o "$RELRO" = "partial" ] ; then
+ printf "\033[32m%-7s\033[m " $RELRO
+ else
+ printf "\033[31m%-7s\033[m " $RELRO
+ fi
+ printf "\033[32m%-4s\033[m" $PIE
+ else
+ # $APP = exec - we want partial relro
+ if [ "$RELRO" = "no" ] ; then
+ printf "\033[31m%-7s\033[m " $RELRO
+ else
+ printf "\033[32m%-7s\033[m " $RELRO
+ fi
+ printf "\033[32m%-4s\033[m" $PIE
+ fi
+ echo
+ else
+ if [ "$APP" = "daemon" -o "$APP" = "setuid" -o "$APP" = "setgid" -o "$APP" = "setcap" -o "$APP" = "network-ip" -o "$APP" = "network-local" ] ; then
+ if [ "$RELRO" = "no" ] ; then
+ RELRO_SUM="no"
+ APP_SUM="$APP"
+ fi
+ if [ "$PIE" = "no" ] ; then
+ PIE_SUM="no"
+ APP_SUM="$APP"
+ fi
+ elif [ "$APP" = "library" ] ; then
+ if [ "$RELRO" = "no" ] ; then
+ RELRO_SUM="no"
+ APP_SUM="$APP"
+ fi
+ # $APP = exec - must have partial or full relro
+ elif [ "$RELRO" = "no" ] ; then
+ RELRO_SUM="no"
+ APP_SUM="$APP"
+ fi
+ fi
+done
+}
+
+if [ "$MODE" = "single" ] ; then
+ printf "%-56s %-10s %-7s %-4s" "FILE" "TYPE" "RELRO" "PIE"
+ echo
+ for i; do
+ f=$(basename $1)
+ # Strip the .rpm extension, if present.
+ do_one ${f%%.rpm}
+ shift
+ done
+ exit 0
+fi
+
+# Skip the kernel as its special
+packages=`rpm -qa --queryformat "%{NAME}.%{ARCH}\n" | egrep -v 'kernel.|debuginfo.|.noarch|gpg-pubkey' | sort`
+printf "%-50s %-5s %-4s %-14s" "PACKAGE" "RELRO" "PIE" "CLASS"
+echo
+for p in $packages
+do
+ RELRO_SUM="yes"
+ PIE_SUM="yes"
+ APP_SUM=""
+ printf "%-50s " $p
+ do_one $p
+ if [[ $skip_current -eq 1 ]] ; then
+ continue
+ fi
+ if [ "$RELRO_SUM" = "yes" ] ; then
+ printf "\033[32m%-5s\033[m " "$RELRO_SUM"
+ else
+ printf "\033[31m%-5s\033[m " "$RELRO_SUM"
+ fi
+ if [ "$PIE_SUM" = "yes" ] ; then
+ printf "\033[32m%-4s\033[m" "$PIE_SUM"
+ if [ "$RELRO_SUM" = "no" ] ; then
+ printf " %-14s" "$APP_SUM"
+ fi
+ else
+ if [ "$APP_SUM" = "network-local" ] ; then
+ printf "\033[33m%-4s\033[m %-14s" "$PIE_SUM" "$APP_SUM"
+ else
+ printf "\033[31m%-4s\033[m %-14s" "$PIE_SUM" "$APP_SUM"
+ fi
+ fi
+ echo
+done
+exit 0
+
+
diff --git a/recipes-security/redhat-security/files/rpm-drop-groups.sh b/recipes-security/redhat-security/files/rpm-drop-groups.sh
new file mode 100644
index 0000000..8c75783
--- /dev/null
+++ b/recipes-security/redhat-security/files/rpm-drop-groups.sh
@@ -0,0 +1,131 @@
+#!/bin/sh
+# rpm-drop-groups
+#
+# Copyright (c) 2011 Steve Grubb. ALL RIGHTS RESERVED.
+# sgrubb@redhat.com
+#
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Given an rpm, it will look at each file to check if it tries to change
+# group and user credentials. If so, it further tries to determine if
+# it also calls setgroups or initgroups. To correctly change groups, the
+# program must drop supplemntal groups. Programs are classified into: n/a
+# meaning no group dropping occurs, yes its done correctly, and no meaning
+# there seems to be a problem.
+#
+# If the --all option is given, it will generate a list of rpms and then
+# summarize the rpm's state. For yes, then all files are in the expected
+# state. Just one program failing can turn the package's summary to no.
+# Re-run passing that package (instead of --all) for the details.
+#
+# To save to file: ./rpm-drop-groups | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" | tee output.txt
+
+VERSION="0.1"
+
+usage () {
+ echo "rpm-drop-groups [--all|<rpmname>|--version]"
+ exit 0
+}
+
+if [ "$1" = "--help" -o $# -eq 0 ] ; then
+ usage
+fi
+if [ "$1" = "--version" ] ; then
+ echo "rpm-drop-groups $VERSION"
+ exit 0
+fi
+if [ "$1" = "--all" ] ; then
+ MODE="all"
+else
+ MODE="single"
+fi
+
+do_one () {
+if ! rpm -q $1 >/dev/null 2>&1 ; then
+ if [ "$MODE" = "single" ] ; then
+ echo "$1 is not installed"
+ exit 1
+ else
+ echo "not installed"
+ return
+ fi
+fi
+files=`rpm -ql $1`
+
+for f in $files
+do
+ if [ ! -f $f ] ; then
+ continue
+ fi
+ if ! file $f | grep -q 'ELF'; then
+ continue
+ fi
+
+ CORRECT="n/a"
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' setgid@.*GLIBC| setegid@.*GLIBC| setresgid@.*GLIBC'`
+ if [ x"$syms" != "x" ] ; then
+ CORRECT="yes"
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' setuid@.*GLIBC| seteuid@.*GLIBC| setresuid@.*GLIBC'`
+ if [ x"$syms" != "x" ] ; then
+ syms=`/usr/bin/readelf -s $f 2>/dev/null | egrep ' setgroups@.*GLIBC| initgroups@.*GLIBC'`
+ if [ x"$syms" = "x" ] ; then
+ syms=`find $f \( -perm -004000 -o -perm -002000 \) -type f -print`
+ if [ x"$syms" = "x" ] ; then
+ CORRECT="no"
+ fi
+ fi
+ fi
+ fi
+
+ # OK, ready for the output
+ if [ "$MODE" = "single" ] ; then
+ printf "%-60s " $f
+ if [ "$CORRECT" = "yes" ] ; then
+ printf "\033[32m%-7s\033[m " $CORRECT
+ elif [ "$CORRECT" = "no" ] ; then
+ printf "\033[31m%-7s\033[m " $CORRECT
+ else
+ printf "\033[33m%-7s\033[m " $CORRECT
+ fi
+ echo
+ else
+ if [ "$CORRECT" = "no" ] ; then
+ CORRECT_SUM="no"
+ fi
+ fi
+done
+}
+
+if [ "$MODE" = "single" ] ; then
+ printf "%-60s%-7s" "FILE" "CORRECT"
+ echo
+ for i; do
+ do_one $1
+ shift
+ done
+ exit 0
+fi
+
+packages=`rpm -qa --queryformat "%{NAME}.%{ARCH}\n" | sort`
+printf "%-50s %-7s" "PACKAGE" "CORRECT"
+echo
+for p in $packages
+do
+ CORRECT_SUM="yes"
+ printf "%-50s " $p
+ do_one $p
+ if [ "$CORRECT_SUM" = "yes" ] ; then
+ printf "\033[32m%-7s\033[m " $CORRECT_SUM
+ else
+ printf "\033[31m%-7s\033[m " $CORRECT_SUM
+ fi
+ echo
+done
+exit 0
+
+
diff --git a/recipes-security/redhat-security/files/selinux-check-devices.sh b/recipes-security/redhat-security/files/selinux-check-devices.sh
new file mode 100644
index 0000000..ef996d7
--- /dev/null
+++ b/recipes-security/redhat-security/files/selinux-check-devices.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+find /dev -context *:device_t:* \( -type c -o -type b \) -printf "%p %Z\n"
+
+
diff --git a/recipes-security/redhat-security/files/selinux-ls-unconfined.sh b/recipes-security/redhat-security/files/selinux-ls-unconfined.sh
new file mode 100644
index 0000000..6868413
--- /dev/null
+++ b/recipes-security/redhat-security/files/selinux-ls-unconfined.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# This software may be freely redistributed under the terms of the GNU
+# public license.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# This checks for unconfined apps running, initrc and inetd are signs
+# of missing transitions.
+
+pidof xinetd >/dev/null
+if [ $? -eq 0 ] ; then
+ps -eZ | egrep "initrc|inetd" | egrep -v `pidof xinetd` | tr ':' ' ' | awk '{ printf "%s %s\n", $3, $NF }'
+else
+ps -eZ | egrep "initrc" | tr ':' ' ' | awk '{ printf "%s %s\n", $3, $NF }'
+fi
+
diff --git a/recipes-security/redhat-security/redhat-security_1.0.bb b/recipes-security/redhat-security/redhat-security_1.0.bb
new file mode 100644
index 0000000..edab390
--- /dev/null
+++ b/recipes-security/redhat-security/redhat-security_1.0.bb
@@ -0,0 +1,38 @@
+DESCRIPTION = "Tools used by redhat linux distribution for security checks"
+SECTION = "security"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
+PR = "r0"
+
+SRC_URI = "file://find-chroot-py.sh \
+ file://find-chroot.sh \
+ file://find-elf4tmp.sh \
+ file://find-execstack.sh \
+ file://find-hidden-exec.sh \
+ file://find-nodrop-groups.sh \
+ file://find-sh4errors.sh \
+ file://find-sh4tmp.sh \
+ file://lib-bin-check.sh \
+ file://rpm-chksec.sh \
+ file://rpm-drop-groups.sh \
+ file://selinux-check-devices.sh \
+ file://selinux-ls-unconfined.sh"
+
+S = "${WORKDIR}"
+
+do_install() {
+ install -d ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-chroot-py.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-chroot.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-elf4tmp.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-execstack.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-hidden-exec.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-nodrop-groups.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-sh4errors.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/find-sh4tmp.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/lib-bin-check.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/rpm-chksec.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/rpm-drop-groups.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/selinux-check-devices.sh ${D}${bindir}
+ install -m 0755 ${WORKDIR}/selinux-ls-unconfined.sh ${D}${bindir}
+}