summaryrefslogtreecommitdiffstats
path: root/recipes-security/bastille
diff options
context:
space:
mode:
authorAndrei Dinu <andrei.adrianx.dinu@intel.com>2013-06-17 17:24:38 +0300
committerAndrei Dinu <andrei.adrianx.dinu@intel.com>2013-06-17 17:24:38 +0300
commit60d90b25631471e8193b3069c6a520ccf7c82008 (patch)
treee413ea3904059ff52a4539aeff358518fa0ae327 /recipes-security/bastille
downloadmeta-security-60d90b25631471e8193b3069c6a520ccf7c82008.tar.gz
meta-security : initial commit
Signed-off-by: Andrei Dinu <andrei.adrianx.dinu@intel.com>
Diffstat (limited to 'recipes-security/bastille')
-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
11 files changed, 7990 insertions, 0 deletions
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 @@
1DESCRIPTION = "Bastille Linux is a Hardening and Reporting/Auditing Program which enhances the security of a Linux box, by configuring daemons, system settings and firewalling."
2LICENSE = "GPLv2"
3LIC_FILES_CHKSUM = "file://${S}/COPYING;md5=c93c0550bd3173f4504b2cbd8991e50b"
4# Bash is needed for set +o privileged (check busybox), might also need ncurses
5RDEPENDS_${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"
6PR = "r0"
7
8inherit allarch
9
10SRC_URI = "http://sourceforge.net/projects/bastille-linux/files/bastille-linux/3.2.1/Bastille-3.2.1.tar.bz2 \
11 file://AccountPermission.pm \
12 file://FileContent.pm \
13 file://HPSpecific.pm \
14 file://Miscellaneous.pm \
15 file://ServiceAdmin.pm \
16 file://config \
17 file://fix_version_parse.patch \
18 file://yocto-standard-patch.patch \
19 file://Curses-and-IOLoader-changes.patch \
20 "
21
22SRC_URI[md5sum] = "df803f7e38085aa5da79f85d0539f91b"
23SRC_URI[sha256sum] = "0ea25191b1dc1c8f91e1b6f8cb5436a3aa1e57418809ef902293448efed5021a"
24
25S = "${WORKDIR}/Bastille"
26
27#CONFFILES_${PN} += "${sysconfdir}/init.d/skeleton"
28#
29#do_compile () {
30# ${CC} ${WORKDIR}/skeleton_test.c -o ${WORKDIR}/skeleton-test
31#}
32#
33do_install () {
34# install -d ${D}${sysconfdir}/init.d
35# cat ${WORKDIR}/skeleton | \
36# sed -e 's,/etc,${sysconfdir},g' \
37# -e 's,/usr/sbin,${sbindir},g' \
38# -e 's,/var,${localstatedir},g' \
39# -e 's,/usr/bin,${bindir},g' \
40# -e 's,/usr,${prefix},g' > ${D}${sysconfdir}/init.d/skeleton
41# chmod a+x ${D}${sysconfdir}/init.d/skeleton
42
43 install -d ${D}${sbindir}
44 install -d ${D}${libdir}/perl/site_perl/Curses
45 ln -sf perl ${D}/${libdir}/perl5
46
47 install -d ${D}${libdir}/Bastille
48 install -d ${D}${libdir}/Bastille/API
49 install -d ${D}${datadir}/Bastille
50 install -d ${D}${datadir}/Bastille/OSMap
51 install -d ${D}${datadir}/Bastille/OSMap/Modules
52 install -d ${D}${datadir}/Bastille/Questions
53 install -d ${D}${datadir}/Bastille/FKL/configs/
54 install -d ${D}${localstatedir}/lock/subsys/bastille
55 install -d ${D}${localstatedir}/log/Bastille
56 install -d ${D}${sysconfdir}/Bastille
57
58 install -m 0755 AutomatedBastille ${D}${sbindir}
59 install -m 0755 BastilleBackEnd ${D}${sbindir}
60 install -m 0755 InteractiveBastille ${D}${sbindir}
61 # Questions.txt has been replaced by Modules.txt and Questions/
62 #install -m 0644 Questions.txt ${D}${datadir}/Bastille
63 install -m 0644 Modules.txt ${D}${datadir}/Bastille
64 # New Weights file(s).
65 install -m 0644 Weights.txt ${D}${datadir}/Bastille
66 # Castle graphic
67 install -m 0644 bastille.jpg ${D}${datadir}/Bastille/
68 # Javascript file
69 install -m 0644 wz_tooltip.js ${D}${datadir}/Bastille/
70 install -m 0644 Credits ${D}${datadir}/Bastille
71 install -m 0644 FKL/configs/fkl_config_redhat.cfg ${D}${datadir}/Bastille/FKL/configs/
72
73 install -m 0755 RevertBastille ${D}${sbindir}
74 install -m 0755 bin/bastille ${D}${sbindir}
75 install -m 0644 bastille-firewall ${D}${datadir}/Bastille
76 install -m 0644 bastille-firewall-reset ${D}${datadir}/Bastille
77 install -m 0644 bastille-firewall-schedule ${D}${datadir}/Bastille
78 install -m 0644 bastille-tmpdir-defense.sh ${D}${datadir}/Bastille
79 install -m 0644 bastille-tmpdir.csh ${D}${datadir}/Bastille
80 install -m 0644 bastille-tmpdir.sh ${D}${datadir}/Bastille
81 install -m 0644 bastille-firewall.cfg ${D}${datadir}/Bastille
82 install -m 0644 bastille-ipchains ${D}${datadir}/Bastille
83 install -m 0644 bastille-netfilter ${D}${datadir}/Bastille
84 install -m 0644 bastille-firewall-early.sh ${D}${datadir}/Bastille
85 install -m 0644 bastille-firewall-pre-audit.sh ${D}${datadir}/Bastille
86 install -m 0644 complete.xbm ${D}${datadir}/Bastille
87 install -m 0644 incomplete.xbm ${D}${datadir}/Bastille
88 install -m 0644 disabled.xpm ${D}${datadir}/Bastille
89 install -m 0644 ifup-local ${D}${datadir}/Bastille
90 install -m 0644 hosts.allow ${D}${datadir}/Bastille
91
92 install -m 0644 Bastille/AccountSecurity.pm ${D}${libdir}/Bastille
93 install -m 0644 Bastille/Apache.pm ${D}${libdir}/Bastille
94 install -m 0644 Bastille/API.pm ${D}${libdir}/Bastille
95 install -m 0644 ${WORKDIR}/AccountPermission.pm ${D}${libdir}/Bastille/API
96 install -m 0644 ${WORKDIR}/FileContent.pm ${D}${libdir}/Bastille/API
97 install -m 0644 ${WORKDIR}/HPSpecific.pm ${D}${libdir}/Bastille/API
98 install -m 0644 ${WORKDIR}/ServiceAdmin.pm ${D}${libdir}/Bastille/API
99 install -m 0644 ${WORKDIR}/Miscellaneous.pm ${D}${libdir}/Bastille/API
100 install -m 0644 Bastille/BootSecurity.pm ${D}${libdir}/Bastille
101 install -m 0644 Bastille/ConfigureMiscPAM.pm ${D}${libdir}/Bastille
102 install -m 0644 Bastille/DisableUserTools.pm ${D}${libdir}/Bastille
103 install -m 0644 Bastille/DNS.pm ${D}${libdir}/Bastille
104 install -m 0644 Bastille/FilePermissions.pm ${D}${libdir}/Bastille
105 install -m 0644 Bastille/FTP.pm ${D}${libdir}/Bastille
106 install -m 0644 Bastille/Firewall.pm ${D}${libdir}/Bastille
107 install -m 0644 Bastille/OSX_API.pm ${D}${libdir}/Bastille
108 install -m 0644 Bastille/LogAPI.pm ${D}${libdir}/Bastille
109 install -m 0644 Bastille/HP_UX.pm ${D}${libdir}/Bastille
110 install -m 0644 Bastille/IOLoader.pm ${D}${libdir}/Bastille
111 install -m 0644 Bastille/Patches.pm ${D}${libdir}/Bastille
112 install -m 0644 Bastille/Logging.pm ${D}${libdir}/Bastille
113 install -m 0644 Bastille/MiscellaneousDaemons.pm ${D}${libdir}/Bastille
114 install -m 0644 Bastille/PatchDownload.pm ${D}${libdir}/Bastille
115 install -m 0644 Bastille/Printing.pm ${D}${libdir}/Bastille
116 install -m 0644 Bastille/PSAD.pm ${D}${libdir}/Bastille
117 install -m 0644 Bastille/RemoteAccess.pm ${D}${libdir}/Bastille
118 install -m 0644 Bastille/SecureInetd.pm ${D}${libdir}/Bastille
119 install -m 0644 Bastille/Sendmail.pm ${D}${libdir}/Bastille
120 install -m 0644 Bastille/TestDriver.pm ${D}${libdir}/Bastille
121 install -m 0644 Bastille/TMPDIR.pm ${D}${libdir}/Bastille
122 install -m 0644 Bastille/test_AccountSecurity.pm ${D}${libdir}/Bastille
123 install -m 0644 Bastille/test_Apache.pm ${D}${libdir}/Bastille
124 install -m 0644 Bastille/test_DNS.pm ${D}${libdir}/Bastille
125 install -m 0644 Bastille/test_FTP.pm ${D}${libdir}/Bastille
126 install -m 0644 Bastille/test_HP_UX.pm ${D}${libdir}/Bastille
127 install -m 0644 Bastille/test_MiscellaneousDaemons.pm ${D}${libdir}/Bastille
128 install -m 0644 Bastille/test_Patches.pm ${D}${libdir}/Bastille
129 install -m 0644 Bastille/test_SecureInetd.pm ${D}${libdir}/Bastille
130 install -m 0644 Bastille/test_Sendmail.pm ${D}${libdir}/Bastille
131 install -m 0644 Bastille/test_BootSecurity.pm ${D}${libdir}/Bastille
132 install -m 0644 Bastille/test_DisableUserTools.pm ${D}${libdir}/Bastille
133 install -m 0644 Bastille/test_FilePermissions.pm ${D}${libdir}/Bastille
134 install -m 0644 Bastille/test_Logging.pm ${D}${libdir}/Bastille
135 install -m 0644 Bastille/test_Printing.pm ${D}${libdir}/Bastille
136 install -m 0644 Bastille/IPFilter.pm ${D}${libdir}/Bastille
137 install -m 0644 Bastille_Curses.pm ${D}${libdir}/perl5/site_perl
138 install -m 0644 Bastille_Tk.pm ${D}${libdir}/perl5/site_perl
139 install -m 0644 Curses/Widgets.pm ${D}${libdir}/perl5/site_perl/Curses
140
141
142
143 install -m 0644 OSMap/LINUX.bastille ${D}${datadir}/Bastille/OSMap
144 install -m 0644 OSMap/LINUX.system ${D}${datadir}/Bastille/OSMap
145 install -m 0644 OSMap/LINUX.service ${D}${datadir}/Bastille/OSMap
146 install -m 0644 OSMap/HP-UX.bastille ${D}${datadir}/Bastille/OSMap
147 install -m 0644 OSMap/HP-UX.system ${D}${datadir}/Bastille/OSMap
148 install -m 0644 OSMap/HP-UX.service ${D}${datadir}/Bastille/OSMap
149 install -m 0644 OSMap/OSX.bastille ${D}${datadir}/Bastille/OSMap
150 install -m 0644 OSMap/OSX.system ${D}${datadir}/Bastille/OSMap
151 install -m 0644 ${WORKDIR}/config ${D}${sysconfdir}/Bastille/config
152
153 for file in `cat Modules.txt` ; do
154 install -m 0644 Questions/$file.txt ${D}${datadir}/Bastille/Questions
155 done
156
157 ln -s ${D}${sbindir}/RevertBastille ${D}${sbindir}/UndoBastille
158}
159
160FILES_${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 @@
1# Copyright (C) 1999-2007 Jay Beale
2# Copyright (C) 2001-2008 Hewlett-Packard Development Company, L.P.
3# Licensed under the GNU General Public License, version 2
4
5package Bastille::API;
6
7## TO DO:
8#
9#
10# 1) Look for more places to insert error handling...
11#
12# 2) Document this module more
13#
14#
15
16
17##########################################################################
18#
19# This module forms the basis for the v1.1 API.
20#
21 ##########################################################################
22
23#
24# This module forms the initial basis for the Bastille Engine, implemented
25# presently via a Perl API for Perl modules.
26#
27# This is still under construction -- it is very usable, but not very well
28# documented, yet.
29#
30
31##########################################################################
32#
33# API Function Listing
34#
35##########################################################################
36# The routines which should be called by Bastille modules are listed here,
37# though they are better documented throughout this file.
38#
39# Distro Specific Stuff:
40#
41# &GetDistro - figures out what distro we're running, if it knows it...
42# &ConfigureForDistro - sets global variables based on the distro
43# &GetGlobal - returns hash values defined in ConfigureForDistro
44#
45# &getGlobalConfig - returns value of hash set up by ReadConfig
46#
47# Logging Specific Stuff has moved to LogAPI.pm:
48#
49# &B_log(type,msg) -- takes care of all logging
50#
51#
52# Input functions for the old input method...
53#
54# File open/close/backup functions
55#
56# &B_open * -- opens a file handle and logs the action/error (OLD WAY!)
57# &B_open_plus -- opens a pair of file handles for the old and new version
58# of a file; respects logonly flag. (NEW WAY)
59# &B_close * -- closes a file handle and logs the action/error (OLD WAY!)
60# &B_close_plus -- closes a pair of file handles opened by B_open_plus,
61# backing up one file and renaming the new file to the
62# old one's name, logging actions/errors. Respects the
63# logonly flag -- needs B_backup file. Finally, sets
64# new file's mode,uid,gid to old file's... (NEW WAY)
65# &B_backup_file - backs up a file that is being changed/deleted into the
66# $GLOBAL_BDIR{"backup"} directory.
67#
68# Non-content file modification functions
69#
70# &B_delete_file - deletes the named file, backing up a copy
71# &B_create_file - creates the named file, if it doesn't exist
72#
73# &B_symlink - create a symlink to a file, recording the revert rm
74#
75# More stuff
76#
77# &B_createdir - make a directory, if it doesn't exist, record revert rmdir
78# &B_cp - copy a file, respecting LOGONLY and revert func.
79# &B_mknod - wrap mknod with revert and logonly and prefix functionality
80#
81# &B_read_sums - reads sum.csv file and parses input into the GLOBAL_SUM hash
82# &B_write_sums - writes sum.csv file from GLOBAL_SUM hash
83# &B_check_sum($) - take a file name and compares the stored cksum with the current
84# cksum of said file
85# &B_set_sum($) - takes a file name and gets that files current cksum then sets
86# that sum in the GLOBAL_SUM hash
87# &B_revert_log - create entry in shell script, executed later by bastille -r
88# &showDisclaimer - Print the disclaimer and wait for 5 minutes for acceptance
89###########################################################################
90# Note: GLOBAL_VERBOSE
91#
92# All logging functions now check GLOBAL_VERBOSE and, if set, will print
93# all the information sent to log files to STDOUT/STDERR as well.
94#
95
96#
97# Note: GLOBAL_LOGONLY
98#
99# All Bastille API functions now check for the existence of a GLOBAL_LOGONLY
100# variable. When said variable is set, no function actually modifies the
101# system.
102#
103# Note: GLOBAL_DEBUG
104#
105# The B_log("DEBUG",...) function now checks GLOBAL_DEBUG and, if set, it will
106# print all the information to a new debug-log file. If GLOBAL_VERBOSE is
107# set it might log to STDOUT/STDERR as well (not yet implemented, pending
108# discussion). Developers should populate appropriate places with &B_log(DEBUG)
109# in order to be able to tell users to use this options and send the logs
110# for inspection and debugging.
111#
112#
113
114
115# Libraries for the Backup_file routine: Cwd and File::Path
116use Cwd;
117use Bastille::OSX_API;
118use Bastille::LogAPI;
119use File::Path;
120use File::Basename;
121
122# Export the API functions listed below for use by the modules.
123
124use Exporter;
125@ISA = qw ( Exporter );
126@EXPORT = qw(
127 setOptions GetDistro ConfigureForDistro B_log B_revert_log
128 SanitizeEnv
129 B_open B_close B_symlink StopLogging
130 B_open_plus B_close_plus
131 B_isFileinSumDB
132 B_create_file B_read_sums B_check_sum B_set_sum isSumDifferent listModifiedFiles
133 B_create_dir B_create_log_file
134 B_delete_file
135 B_cp B_place B_mknod
136 showDisclaimer
137 getSupportedOSHash
138 B_Backtick
139 B_System
140 isProcessRunning
141 checkProcsForService
142
143
144 $GLOBAL_OS $GLOBAL_ACTUAL_OS $CLI
145 $GLOBAL_LOGONLY $GLOBAL_VERBOSE $GLOBAL_DEBUG $GLOBAL_AUDITONLY $GLOBAL_AUDIT_NO_BROWSER $errorFlag
146 %GLOBAL_BIN %GLOBAL_DIR %GLOBAL_FILE
147 %GLOBAL_BDIR %GLOBAL_BFILE
148 %GLOBAL_CONFIG %GLOBAL_SUM
149
150 %GLOBAL_SERVICE %GLOBAL_SERVTYPE %GLOBAL_PROCESS %GLOBAL_RC_CONFIG
151 %GLOBAL_TEST
152
153 getGlobal setGlobal getGlobalConfig
154
155
156 B_parse_fstab
157 B_parse_mtab B_is_rpm_up_to_date
158
159 NOTSECURE_CAN_CHANGE SECURE_CANT_CHANGE
160 NOT_INSTALLED INCONSISTENT MANUAL NOTEST SECURE_CAN_CHANGE
161 STRING_NOT_DEFINED NOT_INSTALLED_NOTSECURE DONT_KNOW
162 RELEVANT_HEADERQ NOTRELEVANT_HEADERQ
163);
164
165
166
167######################################################
168###Testing Functions
169##################################################################
170
171#Define "Constants" for test functions. Note these constants sometimes get
172#interpreted as literal strings when used as hash references, so you may
173# have to use CONSTANT() to disambiguate, like below. Sorry, it was either
174# that or create even *more* global variables.
175# See TestDriver.pm for definitions, and test design doc for full explaination
176use constant {
177 NOTSECURE_CAN_CHANGE => 0,
178 SECURE_CANT_CHANGE => 1,
179 NOT_INSTALLED => 2, # (where the lack makes the system secure, eg telnet)
180 INCONSISTENT => 3,
181 MANUAL => 4,
182 NOTEST => 5,
183 SECURE_CAN_CHANGE => 6,
184 STRING_NOT_DEFINED => 7,
185 NOT_INSTALLED_NOTSECURE => 8, #(Where the missing s/w makes the system less secure eg IPFilter)
186 #Intentional duplicates follow
187 DONT_KNOW => 5,
188 RELEVANT_HEADERQ => 6,
189 NOTRELEVANT_HEADERQ => 0
190};
191
192&SanitizeEnv;
193
194# Set up some common error messages. These are independent of
195# operating system
196
197# These will allow us to line up the warnings and error messages
198my $err ="ERROR: ";
199my $spc =" ";
200my $GLOBAL_OS="None";
201my $GLOBAL_ACTUAL_OS="None";
202my %GLOBAL_SUMS=();
203my $CLI='';
204
205#OS independent Error Messages Follow, normally "bastille" script filters
206#options before interactive or Bastille runs, so this check is often redundant
207$GLOBAL_ERROR{"usage"}="\n".
208 "$spc Usage: bastille [ -b | -c | -x ] [ --os <version> ] [ -f <alternate config> ]\n".
209 "$spc bastille [ -r | --assess | --assessnobowser ]\n\n".
210 "$spc --assess : check status of system and report in browser\n".
211 "$spc --assessnobrowser : check status of system and list report locations\n".
212 "$spc -b : use a saved config file to apply changes\n".
213 "$spc directly to system\n".
214 "$spc -c : use the Curses (non-X11) TUI\n".
215 "$spc -f <alternate config>: populate answers with a different config file\n".
216 "$spc -r : revert all Bastille changes to-date\n".
217 "$spc -x : use the Perl/Tk (X11) GUI\n" .
218 "$spc --os <version> : ask all questions for the given operating system\n" .
219 "$spc version. e.g. --os RH6.0\n";
220
221# These options don't work universally, so it's best not to
222# document them here (yet). Hopefully, we'll get them
223# straightened out soon.
224#"$spc --log : log-only option\n".
225#"$spc -v : verbose mode\n".
226#"$spc --debug : debug mode\n";
227
228
229##############################################################################
230#
231# Directory structure for Bastille Linux v1.2 and up
232#
233##############################################################################
234#
235# /usr/sbin/ -- location of Bastille binaries
236# /usr/lib/Bastille -- location of Bastille modules
237# /usr/share/Bastille -- location of Bastille data files
238# /etc/Bastille -- location of Bastille config files
239#
240# /var/log/Bastille -- location of Bastille log files
241# /var/log/Bastille/revert -- directory holding all Bastille-created revert scripts
242# /var/log/Bastille/revert/backup -- directory holding the original files that
243# Bastille modifies, with permissions intact
244#
245##############################################################################
246
247##############################################################################
248#
249# Directory structure for HP-UX Bastille v2.0 and up
250#
251##############################################################################
252#
253# /opt/sec_mgmt/bastille/bin/ -- location of Bastille binaries
254# /opt/sec_mgmt/bastille/lib/ -- location of Bastille modules
255# /etc/opt/sec_mgmt/bastille/ -- location of Bastille data and config files
256#
257# /var/opt/sec_mgmt/bastille/log/ -- location of Bastille log files
258# /var/opt/sec_mgmt/bastille/revert -- directory holding all Bastille-created
259# revert scripts and save files
260#
261##############################################################################
262
263
264##############################################################################
265##############################################################################
266################## Actual functions start here... ###########################
267##############################################################################
268##############################################################################
269
270###########################################################################
271# setOptions takes six arguments, $GLOBAL_DEBUG, $GLOBAL_LOGONLY,
272# $GLOBAL_VERBOSE, $GLOBAL_AUDITONLY, $GLOBAL_AUDIT_NO_BROWSER, and GLOBAL_OS;
273###########################################################################
274sub setOptions($$$$$$) {
275 ($GLOBAL_DEBUG,$GLOBAL_LOGONLY,$GLOBAL_VERBOSE,$GLOBAL_AUDITONLY,
276 $GLOBAL_AUDIT_NO_BROWSER,$GLOBAL_OS) = @_;
277 if ($GLOBAL_AUDIT_NO_BROWSER) {
278 $GLOBAL_AUDITONLY = 1;
279 }
280 if (not(defined($GLOBAL_OS))){
281 $GLOBAL_OS="None";
282 }
283}
284###########################################################################
285#
286# SanitizeEnv load a proper environment so Bastille cannot be tricked
287# and Perl modules work correctly.
288#
289###########################################################################
290sub SanitizeEnv {
291 delete @ENV{'IFS','CDPATH','ENV','BASH_ENV'};
292 $ENV{CDPATH}=".";
293 $ENV{BASH_ENV}= "";
294 # Bin is needed here or else /usr/lib/perl5/5.005/Cwd.pm
295 # will not find `pwd`
296 # Detected while testing with -w, jfs
297 $ENV{PATH} = "/bin:/usr/bin";
298 # Giorgi, is /usr/local/bin needed? (jfs)
299}
300
301###########################################################################
302#
303# GetDistro checks to see if the target is a known distribution and reports
304# said distribution.
305#
306# This is used throughout the script, but also by ConfigureForDistro.
307#
308#
309###########################################################################
310
311sub GetDistro() {
312
313 my ($release,$distro);
314
315 # Only read files for the distro once.
316 # if the --os option was used then
317 if ($GLOBAL_OS eq "None") {
318 if ( -e "/etc/mandrake-release" ) {
319 open(MANDRAKE_RELEASE,"/etc/mandrake-release");
320 $release=<MANDRAKE_RELEASE>;
321
322 if ( ($release =~ /^Mandrake Linux release (\d+\.\d+\w*)/) or ($release =~ /^Linux Mandrake release (\d+\.\d+\w*)/) ) {
323 $distro="MN$1";
324 }
325 elsif ( $release =~ /^Mandrakelinux release (\d+\.\d+)\b/ ) {
326 $distro="MN$1";
327 }
328 else {
329 print STDERR "$err Couldn't determine Mandrake/Mandriva version! Setting to 10.1!\n";
330 $distro="MN10.1";
331 }
332
333 close(MANDRAKE_RELEASE);
334 }
335 elsif ( -e "/etc/immunix-release" ) {
336 open(IMMUNIX_RELEASE,"/etc/immunix-release");
337 $release=<IMMUNIX_RELEASE>;
338 unless ($release =~ /^Immunix Linux release (\d+\.\d+\w*)/) {
339 print STDERR "$err Couldn't determine Immunix version! Setting to 6.2!\n";
340 $distro="RH6.2";
341 }
342 else {
343 $distro="RH$1";
344 }
345 close(*IMMUNIX_RELEASE);
346 }
347 elsif ( -e '/etc/fedora-release' ) {
348 open(FEDORA_RELEASE,'/etc/fedora-release');
349 $release=<FEDORA_RELEASE>;
350 close FEDORA_RELEASE;
351 if ($release =~ /^Fedora Core release (\d+\.?\d*)/) {
352 $distro = "RHFC$1";
353 }
354 elsif ($release =~ /^Fedora release (\d+\.?\d*)/) {
355 $distro = "RHFC$1";
356 }
357 else {
358 print STDERR "$err Could not determine Fedora version! Setting to Fedora Core 8\n";
359 $distro='RHFC8';
360 }
361 }
362 elsif ( -e "/etc/redhat-release" ) {
363 open(*REDHAT_RELEASE,"/etc/redhat-release");
364 $release=<REDHAT_RELEASE>;
365 if ($release =~ /^Red Hat Linux release (\d+\.?\d*\w*)/) {
366 $distro="RH$1";
367 }
368 elsif ($release =~ /^Red Hat Linux .+ release (\d+)\.?\d*([AEW]S)/) {
369 $distro="RHEL$1$2";
370 }
371 elsif ($release =~ /^Red Hat Enterprise Linux ([AEW]S) release (\d+)/) {
372 $distro="RHEL$2$1";
373 }
374 elsif ($release =~ /^CentOS release (\d+\.\d+)/) {
375 my $version = $1;
376 if ($version =~ /^4\./) {
377 $distro='RHEL4AS';
378 }
379 elsif ($version =~ /^3\./) {
380 $distro='RHEL3AS';
381 }
382 else {
383 print STDERR "$err Could not determine CentOS version! Setting to Red Hat Enterprise 4 AS.\n";
384 $distro='RHEL4AS';
385 }
386 }
387 else {
388 # JJB/HP - Should this be B_log?
389 print STDERR "$err Couldn't determine Red Hat version! Setting to 9!\n";
390 $distro="RH9";
391 }
392 close(REDHAT_RELEASE);
393
394 }
395 elsif ( -e "/etc/debian_version" ) {
396 $stable="3.1"; #Change this when Debian stable changes
397 open(*DEBIAN_RELEASE,"/etc/debian_version");
398 $release=<DEBIAN_RELEASE>;
399 unless ($release =~ /^(\d+\.\d+\w*)/) {
400 print STDERR "$err System is not running a stable Debian GNU/Linux version. Setting to $stable.\n";
401 $distro="DB$stable";
402 }
403 else {
404 $distro="DB$1";
405 }
406 close(DEBIAN_RELEASE);
407 }
408 elsif ( -e "/etc/SuSE-release" ) {
409 open(*SUSE_RELEASE,"/etc/SuSE-release");
410 $release=<SUSE_RELEASE>;
411 if ($release =~ /^SuSE Linux (\d+\.\d+\w*)/i) {
412 $distro="SE$1";
413 }
414 elsif ($release =~ /^SUSE LINUX Enterprise Server (\d+\.?\d?\w*)/i) {
415 $distro="SESLES$1";
416 }
417 elsif ($release =~ /^SUSE Linux Enterprise Server (\d+\.?\d?\w*)/i) {
418 $distro="SESLES$1";
419 }
420 elsif ($release =~ /^openSuSE (\d+\.\d+\w*)/i) {
421 $distro="SE$1";
422 }
423 else {
424 print STDERR "$err Couldn't determine SuSE version! Setting to 10.3!\n";
425 $distro="SE10.3";
426 }
427 close(SUSE_RELEASE);
428 }
429 elsif ( -e "/etc/turbolinux-release") {
430 open(*TURBOLINUX_RELEASE,"/etc/turbolinux-release");
431 $release=<TURBOLINUX_RELEASE>;
432 unless ($release =~ /^Turbolinux Workstation (\d+\.\d+\w*)/) {
433 print STDERR "$err Couldn't determine TurboLinux version! Setting to 7.0!\n";
434 $distro="TB7.0";
435 }
436 else {
437 $distro="TB$1";
438 }
439 close(TURBOLINUX_RELEASE);
440 }
441 else {
442 # We're either on Mac OS X, HP-UX or an unsupported O/S.
443 if ( -x '/usr/bin/uname') {
444 # uname is in /usr/bin on Mac OS X and HP-UX
445 $release=`/usr/bin/uname -sr`;
446 }
447 else {
448 print STDERR "$err Could not determine operating system version!\n";
449 $distro="unknown";
450 }
451
452 # Figure out what kind of system we're on.
453 if ($release ne "") {
454 if ($release =~ /^Darwin\s+(\d+)\.(\d+)/) {
455 if ($1 == 6 ) {
456 $distro = "OSX10.2";
457 }
458 elsif ($1 == 7) {
459 $distro = "OSX10.3";
460 }
461 elsif ($1 == 8) {
462 $distro = "OSX10.3";
463 }
464 else {
465 $distro = "unknown";
466 }
467 }
468 elsif ( $release =~ /(^HP-UX)\s*B\.(\d+\.\d+)/ ) {
469 $distro="$1$2";
470 }
471 else {
472 print STDERR "$err Could not determine operating system version!\n";
473 $distro="unknown";
474 }
475 }
476 }
477
478 $GLOBAL_OS=$distro;
479 } elsif (not (defined $GLOBAL_OS)) {
480 print "ERROR: GLOBAL OS Scoping Issue\n";
481 } else {
482 $distro = $GLOBAL_OS;
483 }
484
485 return $distro;
486}
487
488###################################################################################
489# &getActualDistro; #
490# #
491# This subroutine returns the actual os version in which is running on. This #
492# os version is independent of the --os switch feed to bastille. #
493# #
494###################################################################################
495sub getActualDistro {
496 # set local variable to $GLOBAL_OS
497
498 if ($GLOBAL_ACTUAL_OS eq "None") {
499 my $os = $GLOBAL_OS;
500 # undef GLOBAL_OS so that the GetDistro routine will return
501 # the actualDistro, it might otherwise return the distro set
502 # by the --os switch.
503 $GLOBAL_OS = "None";
504 $GLOBAL_ACTUAL_OS = &GetDistro;
505 # reset the GLOBAL_OS variable
506 $GLOBAL_OS = $os;
507 }
508 return $GLOBAL_ACTUAL_OS;
509}
510# These are helper routines which used to be included inside GetDistro
511sub is_OS_supported($) {
512 my $os=$_[0];
513 my $supported=0;
514 my %supportedOSHash = &getSupportedOSHash;
515
516 foreach my $oSType (keys %supportedOSHash) {
517 foreach my $supported_os ( @{$supportedOSHash{$oSType}} ) {
518 if ( $supported_os eq $os ) {
519 $supported=1;
520 }
521 }
522 }
523
524 return $supported;
525}
526
527###############################################################################
528# getSupportedOSHash
529#
530# This subrountine returns a hash of supported OSTypes, which point to a
531# a list of supported distros. When porting to a new distro, add the
532# distro id to the hash in its appropriate list.
533###############################################################################
534sub getSupportedOSHash () {
535
536 my %osHash = ("LINUX" => [
537 "DB2.2", "DB3.0",
538 "RH6.0","RH6.1","RH6.2","RH7.0",
539 "RH7.1","RH7.2","RH7.3","RH8.0",
540 "RH9",
541 "RHEL5",
542 "RHEL4AS","RHEL4ES","RHEL4WS",
543 "RHEL3AS","RHEL3ES","RHEL3WS",
544 "RHEL2AS","RHEL2ES","RHEL2WS",
545 "RHFC1","RHFC2","RHFC3","RHFC4",
546 "RHFC5","RHFC6","RHFC7","RHFC8",
547 "MN6.0","MN6.1 ","MN7.0","MN7.1",
548 "MN7.2","MN8.0","MN8.1","MN8.2",
549 "MN10.1",
550 "SE7.2","SE7.3", "SE8.0","SE8.1","SE9.0","SE9.1",
551 "SE9.2","SE9.3","SE10.0","SE10.1","SE10.2","SE10.3",
552 "SESLES8","SESLES9","SESLES10",
553 "TB7.0"
554 ],
555
556 "HP-UX" => [
557 "HP-UX11.00","HP-UX11.11",
558 "HP-UX11.22", "HP-UX11.23",
559 "HP-UX11.31"
560 ],
561
562 "OSX" => [
563 'OSX10.2','OSX10.3','OSX10.4'
564 ]
565 );
566
567 return %osHash;
568
569}
570
571
572###############################################################################
573# setFileLocations(OSMapFile, currentDistro);
574#
575# Given a file map location this subroutine will create the GLOBAL_*
576# hash entries specified within this file.
577###############################################################################
578sub setFileLocations($$) {
579
580 my ($fileInfoFile,$currentDistro) = @_;
581
582 # define a mapping from the first argument to the proper hash
583 my %map = ("BIN" => \%GLOBAL_BIN,
584 "FILE" => \%GLOBAL_FILE,
585 "BFILE" => \%GLOBAL_BFILE,
586 "DIR" => \%GLOBAL_DIR,
587 "BDIR" => \%GLOBAL_BDIR
588 );
589 my @fileInfo = ();
590
591 # File containing file location information
592 if(open(FILEINFO, "<$fileInfoFile" )) {
593
594 @fileInfo = <FILEINFO>;
595
596 close(FILEINFO);
597
598 }
599 else {
600 print STDERR "$err Unable to find file location information for '$distro'.\n" .
601 "$spc Contact the Bastille support list for details.\n";
602 exit(1);
603 }
604
605 # Each line of the file map follows the pattern below:
606 # bdir,init.d,'/etc/rc.d/init.d',RH7.2,RH7.3
607 # if the distro information is not available, e.g.
608 # bdir,init.d,'/etc/rc.d/init.d'
609 # then the line applies to all distros under the OSType
610 foreach my $file (@fileInfo) {
611 # Perl comments are allowed within the file but only entire line comments
612 if($file !~ /^\s+\#|^\s+$/) {
613 chomp $file;
614 # type relates to the map above, type bin will map to GLOBAL_BIN
615 # id is the identifier used as the hash key by the GLOBAL hash
616 # fileLocation is the full path to the file
617 # distroList is an optional list of distros that this particular
618 # file location, if no distro list is presented the file location
619 # is considered to apply to all distros
620 my ($type,$id,$fileLocation,@distroList) = split /\s*,\s*/, $file;
621 $fileLocation =~ s/^\'(.*)\'$/$1/;
622 if($#distroList == -1) {
623 $map{uc($type)}->{$id}=$fileLocation;
624 }
625 else {
626 foreach my $distro (@distroList) {
627 # if the current distro matches the distro listed then
628 # this file location applies
629 if($currentDistro =~ /$distro/) {
630 $map{uc($type)}->{$id}=$fileLocation;
631 }
632 }
633 }
634 }
635 }
636 unless(defined($map{uc("BFILE")}->{"current_config"})) {
637 &setGlobal("BFILE","current_config",&getGlobal("BFILE","config"));
638 }
639}
640
641###############################################################################
642# setServiceInfo($OSServiceMapFile, $currentDistro
643#
644# Given the location of an OS Service map file, which describes
645# a service in terms of configurables, processes and a service type.
646# The subroutine fills out the GLOBAL_SERVICE, $GLOBAL_RC_CONFIG, GLOBAL_SERVTYPE, and
647# GLOBAL_PROCESS hashes for a given service ID.
648###############################################################################
649sub setServiceInfo($$) {
650 my ($serviceInfoFile,$currentDistro) = @_;
651 my @serviceInfo = ();
652
653 if(open(SERVICEINFO, "<$serviceInfoFile" )) {
654
655 @serviceInfo = <SERVICEINFO>;
656
657 close(SERVICEINFO);
658
659 }
660 else {
661 print STDERR "$err Unable to find service, service type, and process information\n" .
662 "$spc for '$distro'.\n" .
663 "$spc Contact the Bastille support list for details.\n";
664 exit(1);
665 }
666
667
668 # The following loop, parses the entire (YOUR OS).service file
669 # to provide service information for YOUR OS.
670 # The files format is as follows:
671 # serviceID,servType,('service' 'configuration' 'list'),('process' 'list')[,DISTROS]*
672 # if distros are not present then the service is assumed to be
673 # relevant the the current distro
674
675
676#
677# More specifically, this file's format for rc-based daemons is:
678#
679# script_name,rc,(rc-config-file rc-config-file ...),(rc-variable1 rc-variable2 ...),('program_name1 program_name2 ...')
680#
681# ...where script_name is a file in /etc/init.d/ and
682# ...program_nameN is a program launced by the script.
683#
684# This file's format for inet-based daemons is:
685#
686# identifier, inet, line name/file name, program name
687#
688# label,inet,(port1 port2 ...),(daemon1 daemon2 ...)
689#
690# ...where label is arbitrary, portN is one of the ports
691# ...this one listens on, and daemonN is a program launched
692# ...in response to a connection on a port.
693
694 foreach my $service (@serviceInfo) {
695 # This file accepts simple whole line comments perl style
696 if($service !~ /^\s+\#|^\s+$/) {
697 chomp $service;
698 my ($serviceID,$servType,$strConfigList,$strServiceList,
699 $strProcessList,@distroList) = split /\s*,\s*/, $service;
700
701 sub MakeArrayFromString($){
702 my $entryString = $_[0];
703 my @destArray = ();
704 if ($entryString =~ /\'\S+\'/) { #Make sure we have something to extract before we try
705 @destArray = split /\'\s+\'/, $entryString;
706 $destArray[0] =~ s/^\(\'(.+)$/$1/; # Remove leading quotation mark
707 $destArray[$#destArray] =~ s/^(.*)\'\)$/$1/; #Remove trailing quotation mark
708 }
709 return @destArray;
710 }
711
712 # produce a list of configuration files from the files
713 # format ('configuration' 'files')
714 my @configList = MakeArrayFromString($strConfigList);
715
716 # produce a list of service configurables from the files
717 # format ('service' 'configurable')
718 my @serviceList = MakeArrayFromString($strServiceList);
719
720 # produce a list of process names from the files format
721 # ('my' 'process' 'list')
722 my @processList = MakeArrayFromString($strProcessList);
723
724 # if distros were not specified then accept the service information
725 if($#distroList == -1) {
726 @{$GLOBAL_SERVICE{$serviceID}} = @serviceList;
727 $GLOBAL_SERVTYPE{$serviceID} = $servType;
728 @{$GLOBAL_PROCESS{$serviceID}} = @processList;
729 @{$GLOBAL_RC_CONFIG{$serviceID}} = @configList;
730 }
731 else {
732 # only if the current distro matches one of the listed distros
733 # include the service information.
734 foreach my $distro (@distroList) {
735 if($currentDistro =~ /$distro/) {
736 @{$GLOBAL_SERVICE{$serviceID}} = @serviceList;
737 $GLOBAL_SERVTYPE{$serviceID} = $servType;
738 @{$GLOBAL_PROCESS{$serviceID}} = @processList;
739 @{$GLOBAL_RC_CONFIG{$serviceID}} = @configList;
740 }
741 }
742 }
743 }
744 }
745}
746
747
748
749###############################################################################
750# getFileAndServiceInfo($distro,$actualDistro)
751#
752# This subrountine, given distribution information, will import system file
753# and service information into the GLOBA_* hashes.
754#
755# NOTE: $distro and $actualDistro will only differ when the --os switch is
756# used to generate a configuration file for an arbitrary operating
757# system.
758#
759###############################################################################
760sub getFileAndServiceInfo($$) {
761
762 my ($distro,$actualDistro) = @_;
763
764 # defines the path to the OS map information for any supported OS type.
765 # OS map information is used to determine file locations for a given
766 # distribution.
767 my %oSInfoPath = (
768 "LINUX" => "/usr/share/Bastille/OSMap/",
769 "HP-UX" => "/etc/opt/sec_mgmt/bastille/OSMap/",
770 "OSX" => "/usr/share/Bastille/OSMap/"
771 );
772
773 # returns the OS, LINUX, HP-UX, or OSX, associated with this
774 # distribution
775 my $actualOS = &getOSType($actualDistro);
776 my $oS = &getOSType($distro);
777
778 if(defined $actualOS && defined $oS) {
779 my $bastilleInfoFile = $oSInfoPath{$actualOS} . "${actualOS}.bastille";
780 my $systemInfoFile = $oSInfoPath{$actualOS} . "${oS}.system";
781 my $serviceInfoFile = $oSInfoPath{$actualOS} . "${oS}.service";
782
783 if(-f $bastilleInfoFile) {
784 &setFileLocations($bastilleInfoFile,$actualDistro);
785 }
786 else {
787 print STDERR "$err Unable to find bastille file information.\n" .
788 "$spc $bastilleInfoFile does not exist on the system";
789 exit(1);
790 }
791
792 if(-f $systemInfoFile) {
793 &setFileLocations($systemInfoFile,$distro);
794 }
795 else {
796 print STDERR "$err Unable to find system file information.\n" .
797 "$spc $systemInfoFile does not exist on the system";
798 exit(1);
799 }
800 # Service info File is optional
801 if(-f $serviceInfoFile) {
802 &setServiceInfo($serviceInfoFile,$distro);
803 }
804 }
805 else {
806 print STDERR "$err Unable to determine operating system type\n" .
807 "$spc for $actualDistro or $distro\n";
808 exit(1);
809 }
810
811}
812
813
814# returns the Operating System type associated with the specified
815# distribution.
816sub getOSType($) {
817
818 my $distro = $_[0];
819
820 my %supportedOSHash = &getSupportedOSHash;
821 foreach my $oSType (keys %supportedOSHash) {
822 foreach my $oSDistro (@{$supportedOSHash{$oSType}}) {
823 if($distro eq $oSDistro) {
824 return $oSType;
825 }
826 }
827 }
828
829 return undef;
830
831}
832
833
834# Test subroutine used to debug file location info for new Distributions as
835# they are ported.
836sub dumpFileInfo {
837 print "Dumping File Information\n";
838 foreach my $hashref (\%GLOBAL_BIN,\%GLOBAL_DIR,\%GLOBAL_FILE,\%GLOBAL_BFILE,\%GLOBAL_BDIR) {
839 foreach my $id (keys %{$hashref}) {
840 print "$id: ${$hashref}{$id}\n";
841 }
842 print "-----------------------\n\n";
843 }
844}
845
846# Test subroutine used to debug service info for new Distributions as
847# they are ported.
848sub dumpServiceInfo {
849 print "Dumping Service Information\n";
850 foreach my $serviceId (keys %GLOBAL_SERVICE) {
851 print "$serviceId:\n";
852 print "Type - $GLOBAL_SERVTYPE{$serviceId}\n";
853 print "Service List:\n";
854 foreach my $service (@{$GLOBAL_SERVICE{$serviceId}}) {
855 print "$service ";
856 }
857 print "\nProcess List:\n";
858 foreach my $process (@{$GLOBAL_PROCESS{$serviceId}}) {
859 print "$process ";
860 }
861 print "\n----------------------\n";
862 }
863}
864
865
866###########################################################################
867#
868# &ConfigureForDistro configures the API for a given distribution. This
869# includes setting global variables that tell the Bastille API about
870# given binaries and directories.
871#
872# WARNING: If a distro is not covered here, Bastille may not be 100%
873# compatible with it, though 1.1 is written to be much smarter
874# about unknown distros...
875#
876###########################################################################
877sub ConfigureForDistro {
878
879 my $retval=1;
880
881 # checking to see if the os version given is in fact supported
882 my $distro = &GetDistro;
883
884 # checking to see if the actual os version is in fact supported
885 my $actualDistro = &getActualDistro;
886 $ENV{'LOCALE'}=''; # So that test cases checking for english results work ok.
887 if ((! &is_OS_supported($distro)) or (! &is_OS_supported($actualDistro)) ) {
888 # if either is not supported then print out a list of supported versions
889 if (! &is_OS_supported($distro)) {
890 print STDERR "$err '$distro' is not a supported operating system.\n";
891 }
892 else {
893 print STDERR "$err Bastille is unable to operate correctly on this\n";
894 print STDERR "$spc $distro operating system.\n";
895 }
896 my %supportedOSHash = &getSupportedOSHash;
897 print STDERR "$spc Valid operating system versions are as follows:\n";
898
899 foreach my $oSType (keys %supportedOSHash) {
900
901 print STDERR "$spc $oSType:\n$spc ";
902
903 my $os_number = 1;
904 foreach my $os (@{$supportedOSHash{$oSType}}) {
905 print STDERR "'$os' ";
906 if ($os_number == 5){
907 print STDERR "\n$spc ";
908 $os_number = 1;
909 }
910 else {
911 $os_number++;
912 }
913
914 }
915 print STDERR "\n";
916 }
917
918 print "\n" . $GLOBAL_ERROR{"usage"};
919 exit(1);
920 }
921
922 # First, let's make sure that we do not create any files or
923 # directories with more permissive permissions than we
924 # intend via setting the Perl umask
925 umask(077);
926
927 &getFileAndServiceInfo($distro,$actualDistro);
928
929# &dumpFileInfo; # great for debuging file location issues
930# &dumpServiceInfo; # great for debuging service information issues
931
932 # OS dependent error messages (after configuring file locations)
933 my $nodisclaim_file = &getGlobal('BFILE', "nodisclaimer");
934
935 $GLOBAL_ERROR{"disclaimer"}="$err Unable to touch $nodisclaim_file:" .
936 "$spc You must use Bastille\'s -n flag (for example:\n" .
937 "$spc bastille -f -n) or \'touch $nodisclaim_file \'\n";
938
939 return $retval;
940}
941
942
943###########################################################################
944###########################################################################
945# #
946# The B_<perl_function> file utilities are replacements for their Perl #
947# counterparts. These replacements log their actions and their errors, #
948# but are very similar to said counterparts. #
949# #
950###########################################################################
951###########################################################################
952
953
954###########################################################################
955# B_open is used for opening a file for reading. B_open_plus is the preferred
956# function for writing, since it saves a backup copy of the file for
957# later restoration.
958#
959# B_open opens the given file handle, associated with the given filename
960# and logs appropriately.
961#
962###########################################################################
963
964sub B_open {
965 my $retval=1;
966 my ($handle,$filename)=@_;
967
968 unless ($GLOBAL_LOGONLY) {
969 $retval = open $handle,$filename;
970 }
971
972 ($handle) = "$_[0]" =~ /[^:]+::[^:]+::([^:]+)/;
973 &B_log("ACTION","open $handle,\"$filename\";\n");
974 unless ($retval) {
975 &B_log("ERROR","open $handle, $filename failed...\n");
976 }
977
978 return $retval;
979}
980
981###########################################################################
982# B_open_plus is the v1.1 open command.
983#
984# &B_open_plus($handle_file,$handle_original,$file) opens the file $file
985# for reading and opens the file ${file}.bastille for writing. It is the
986# counterpart to B_close_plus, which will move the original file to
987# $GLOBAL_BDIR{"backup"} and will place the new file ${file}.bastille in its
988# place.
989#
990# &B_open_plus makes the appropriate log entries in the action and error
991# logs.
992###########################################################################
993
994sub B_open_plus {
995
996 my ($handle_file,$handle_original,$file)=@_;
997 my $retval=1;
998 my $return_file=1;
999 my $return_old=1;
1000
1001 my $original_file = $file;
1002
1003 # Open the original file and open a copy for writing.
1004 unless ($GLOBAL_LOGONLY) {
1005 # if the temporary filename already exists then the open operation will fail.
1006 if ( $file eq "" ){
1007 &B_log("ERROR","Internal Error - Attempt Made to Open Blank Filename");
1008 $return_old=0;
1009 $return_file=0;
1010 return 0; #False
1011 } elsif (-e "${file}.bastille") {
1012 &B_log("ERROR","Unable to open $file as the swap file ".
1013 "${file}.bastille\" already exists. Rename the swap ".
1014 "file to allow Bastille to make desired file modifications.");
1015 $return_old=0;
1016 $return_file=0;
1017 }
1018 else {
1019 $return_old = open $handle_original,"$file";
1020 $return_file = open $handle_file,("> $file.bastille");
1021 }
1022 }
1023
1024 # Error handling/logging here...
1025 #&B_log("ACTION","# Modifying file $original_file via temporary file $original_file.bastille\n");
1026 unless ($return_file) {
1027 $retval=0;
1028 &B_log("ERROR","open file: \"$original_file.bastille\" failed...\n");
1029 }
1030 unless ($return_old) {
1031 $retval=0;
1032 &B_log("ERROR","open file: \"$original_file\" failed.\n");
1033 }
1034
1035 return $retval;
1036
1037}
1038
1039###########################################################################
1040# B_close was the v1.0 close command. It is still used in places in the
1041# code.
1042# However the use of B _close_plus, which implements a new, smarter,
1043# backup scheme is preferred.
1044#
1045# B_close closes the given file handle, associated with the given filename
1046# and logs appropriately.
1047###########################################################################
1048
1049
1050sub B_close {
1051 my $retval=1;
1052
1053 unless ($GLOBAL_LOGONLY) {
1054 $retval = close $_[0];
1055 }
1056
1057 &B_log("ACTION", "close $_[0];\n");
1058 unless ($retval) {
1059 &B_log("ERROR", "close $_[0] failed...\n");
1060 }
1061
1062 return $retval;
1063}
1064
1065
1066###########################################################################
1067# B_close_plus is the v1.1 close command.
1068#
1069# &B_close_plus($handle_file,$handle_original,$file) closes the files
1070# $file and ${file}.bastille, backs up $file to $GLOBAL_BDIR{"backup"} and
1071# renames ${file}.bastille to $file. This backup is made using the
1072# internal API function &B_backup_file. Further, it sets the new file's
1073# permissions and uid/gid to the same as the old file.
1074#
1075# B_close_plus is the counterpart to B_open_plus, which opened $file and
1076# $file.bastille with the file handles $handle_original and $handle_file,
1077# respectively.
1078#
1079# &B_close_plus makes the appropriate log entries in the action and error
1080# logs.
1081###########################################################################
1082
1083sub B_close_plus {
1084 my ($handle_file,$handle_original,$file)=@_;
1085 my ($mode,$uid,$gid);
1086 my @junk;
1087
1088 my $original_file;
1089
1090 my $retval=1;
1091 my $return_file=1;
1092 my $return_old=1;
1093
1094 # Append the global prefix, but save the original for B_backup_file b/c
1095 # it appends the prefix on its own...
1096
1097 $original_file=$file;
1098
1099 #
1100 # Close the files and prepare for the rename
1101 #
1102
1103 if (($file eq "") or (not(-e $file ))) {
1104 &B_log("ERROR","Internal Error, attempted to close a blank filename ".
1105 "or nonexistent file.");
1106 return 0; #False
1107 }
1108
1109 unless ($GLOBAL_LOGONLY) {
1110 $return_file = close $handle_file;
1111 $return_old = close $handle_original;
1112 }
1113
1114 # Error handling/logging here...
1115 #&B_log("ACTION","#Closing $original_file and backing up to " . &getGlobal('BDIR', "backup"));
1116 #&B_log("ACTION","/$original_file\n");
1117
1118 unless ($return_file) {
1119 $retval=0;
1120 &B_log("ERROR","close $original_file failed...\n");
1121 }
1122 unless ($return_old) {
1123 $retval=0;
1124 &B_log("ERROR","close $original_file.bastille failed.\n");
1125 }
1126
1127 #
1128 # If we've had no errors, backup the old file and put the new one
1129 # in its place, with the Right permissions.
1130 #
1131
1132 unless ( ($retval == 0) or $GLOBAL_LOGONLY) {
1133
1134 # Read the permissions/owners on the old file
1135
1136 @junk=stat ($file);
1137 $mode=$junk[2];
1138 $uid=$junk[4];
1139 $gid=$junk[5];
1140
1141 # Set the permissions/owners on the new file
1142
1143 chmod $mode, "$file.bastille" or &B_log("ERROR","Not able to retain permissions on $original_file!!!\n");
1144 chown $uid, $gid, "$file.bastille" or &B_log("ERROR","Not able to retain owners on $original_file!!!\n");
1145
1146 # Backup the old file and put a new one in place.
1147
1148 &B_backup_file($original_file);
1149 rename "$file.bastille", $file or
1150 &B_log("ERROR","B_close_plus: not able to move $original_file.bastille to $original_file\n");
1151
1152 # We add the file to the GLOBAL_SUMS hash if it is not already present
1153 &B_set_sum($file);
1154
1155 }
1156
1157 return $retval;
1158}
1159
1160###########################################################################
1161# &B_backup_file ($file) makes a backup copy of the file $file in
1162# &getGlobal('BDIR', "backup"). Note that this routine is intended for internal
1163# use only -- only Bastille API functions should call B_backup_file.
1164#
1165###########################################################################
1166
1167sub B_backup_file {
1168
1169 my $file=$_[0];
1170 my $complain = 1;
1171 my $original_file = $file;
1172
1173 my $backup_dir = &getGlobal('BDIR', "backup");
1174 my $backup_file = $backup_dir . $original_file;
1175
1176 my $retval=1;
1177
1178 # First, separate the file into the directory and the relative filename
1179
1180 my $directory ="";
1181 if ($file =~ /^(.*)\/([^\/]+)$/) {
1182 #$relative_file=$2;
1183 $directory = $1;
1184 } else {
1185 $directory=cwd;
1186 }
1187
1188 # Now, if the directory does not exist, create it.
1189 # Later:
1190 # Try to set the same permissions on the patch directory that the
1191 # original had...?
1192
1193 unless ( -d ($backup_dir . $directory) ) {
1194 mkpath(( $backup_dir . $directory),0,0700);
1195
1196 }
1197
1198 # Now we backup the file. If there is already a backup file there,
1199 # we will leave it alone, since it exists from a previous run and
1200 # should be the _original_ (possibly user-modified) distro's version
1201 # of the file.
1202
1203 if ( -e $file ) {
1204
1205 unless ( -e $backup_file ) {
1206 my $command=&getGlobal("BIN","cp");
1207 &B_Backtick("$command -p $file $backup_file");
1208 &B_revert_log (&getGlobal("BIN","mv"). " $backup_file $file");
1209 }
1210
1211 } else {
1212 # The file we were trying to backup doesn't exist.
1213
1214 $retval=0;
1215 # This is a non-fatal error, not worth complaining about
1216 $complain = 0;
1217 #&ErrorLog ("# Failed trying to backup file $file -- it doesn't exist!\n");
1218 }
1219
1220 # Check to make sure that the file does exist in the backup location.
1221
1222 unless ( -e $backup_file ) {
1223 $retval=0;
1224 if ( $complain == 1 ) {
1225 &B_log("ERROR","Failed trying to backup $file -- the copy was not created.\n");
1226 }
1227 }
1228
1229 return $retval;
1230}
1231
1232
1233###########################################################################
1234# &B_read_sums reads in the sum.csv file which contains information
1235# about Bastille modified files. The file structure is as follows:
1236#
1237# filename,filesize,cksum
1238#
1239# It reads the information into the GLOBAL_SUM hash i.e.
1240# $GLOBAL_SUM{$file}{sum} = $cksum
1241# $GLOBAL_SUM{$file}{filesize} = $size
1242# For the first run of Bastille on a given system this subroutine
1243# is a no-op, and returns "undefined."
1244###########################################################################
1245
1246sub B_read_sums {
1247
1248 my $sumFile = &getGlobal('BFILE',"sum.csv");
1249
1250 if ( -e $sumFile ) {
1251
1252 open( SUM, "< $sumFile") or &B_log("ERROR","Unable to open $sumFile for read.\n$!\n");
1253
1254 while( my $line = <SUM> ) {
1255 chomp $line;
1256 my ($file,$filesize,$sum,$flag) = split /,/, $line;
1257 if(-e $file) {
1258 $GLOBAL_SUM{"$file"}{filesize} = $filesize;
1259 $GLOBAL_SUM{"$file"}{sum} = $sum;
1260 }
1261 }
1262
1263 close(SUM);
1264 } else {
1265 return undef;
1266 }
1267}
1268
1269
1270###########################################################################
1271# &B_write_sums writes out the sum.csv file which contains information
1272# about Bastille modified files. The file structure is as follows:
1273#
1274# filename,filesize,cksum
1275#
1276# It writes the information from the GLOBAL_SUM hash i.e.
1277#
1278# $file,$GLOBAL_SUM{$file}{sum},$GLOBAL_SUM{$file}{filesize}
1279#
1280# This subroutine requires access to the GLOBAL_SUM hash.
1281###########################################################################
1282
1283sub B_write_sums {
1284
1285 my $sumFile = &getGlobal('BFILE',"sum.csv");
1286
1287 if ( %GLOBAL_SUM ) {
1288
1289 open( SUM, "> $sumFile") or &B_log("ERROR","Unable to open $sumFile for write.\n$!\n");
1290
1291 for my $file (sort keys %GLOBAL_SUM) {
1292 if( -e $file) {
1293 print SUM "$file,$GLOBAL_SUM{\"$file\"}{filesize},$GLOBAL_SUM{\"$file\"}{sum}\n";
1294 }
1295 }
1296
1297 close(SUM);
1298 }
1299
1300}
1301
1302
1303###########################################################################
1304# &B_check_sum($file) compares the stored cksum and filesize of the given
1305# file compared to the current cksum and filesize respectively.
1306# This subroutine also keeps the state of the sum check by setting the
1307# checked flag which tells the subroutine that on this run this file
1308# has already been checked.
1309#
1310# $GLOBAL_SUM{$file}{checked} = 1;
1311#
1312# This subroutine requires access to the GLOBAL_SUM hash.
1313#
1314# Returns 1 if sum checks out and 0 if not
1315###########################################################################
1316
1317sub B_check_sum($) {
1318 my $file = $_[0];
1319 my $cksum = &getGlobal('BIN',"cksum");
1320
1321 if (not(%GLOBAL_SUM)) {
1322 &B_read_sums;
1323 }
1324
1325 if(-e $file) {
1326 my ($sum,$size,$ckfile) = split(/\s+/, `$cksum $file`);
1327 my $commandRetVal = ($? >> 8); # find the command's return value
1328
1329 if($commandRetVal != 0) {
1330 &B_log("ERROR","$cksum reported the following error:\n$!\n");
1331 return 0;
1332 } else {
1333 if ( exists $GLOBAL_SUM{$file} ) {
1334 # if the file size or file sum differ from those recorded.
1335 if (( $GLOBAL_SUM{$file}{filesize} == $size) and
1336 ($GLOBAL_SUM{$file}{sum} == $sum )) {
1337 return 1; #True, since saved state matches up, all is well.
1338 } else {
1339 return 0; #False, since saved state doesn't match
1340 }
1341 } else {
1342 &B_log("ERROR","File: $file does not exist in sums database.");
1343 return 0;
1344 }
1345 }
1346 } else {
1347 &B_log("ERROR","The file: $file does not exist for comparison in B_check_sum.");
1348 return 0;
1349 }
1350}
1351
1352# Don't think we need this anymore as function now check_sums returns
1353# results directly
1354#sub isSumDifferent($) {
1355# my $file = $_[0];
1356# if(exists $GLOBAL_SUM{$file}) {
1357# return $GLOBAL_SUM{$file}{flag}
1358# }
1359#}
1360
1361sub listModifiedFiles {
1362 my @listModifiedFiles=sort keys %GLOBAL_SUM;
1363 return @listModifiedFiles;
1364}
1365
1366###########################################################################
1367# &B_isFileinSumDB($file) checks to see if a given file's sum was saved.
1368#
1369# $GLOBAL_SUM{$file}{filesize} = $size;
1370# $GLOBAL_SUM{$file}{sum} = $cksum;
1371#
1372# This subroutine requires access to the GLOBAL_SUM hash.
1373###########################################################################
1374
1375sub B_isFileinSumDB($) {
1376 my $file = $_[0];
1377
1378 if (not(%GLOBAL_SUM)) {
1379 &B_log("DEBUG","Reading in DB from B_isFileinSumDB");
1380 &B_read_sums;
1381 }
1382 if (exists($GLOBAL_SUM{"$file"})){
1383 &B_log("DEBUG","$file is in sum database");
1384 return 1; #true
1385 } else {
1386 &B_log("DEBUG","$file is not in sum database");
1387 return 0; #false
1388 }
1389}
1390
1391###########################################################################
1392# &B_set_sum($file) sets the current cksum and filesize of the given
1393# file into the GLOBAL_SUM hash.
1394#
1395# $GLOBAL_SUM{$file}{filesize} = $size;
1396# $GLOBAL_SUM{$file}{sum} = $cksum;
1397#
1398# This subroutine requires access to the GLOBAL_SUM hash.
1399###########################################################################
1400
1401sub B_set_sum($) {
1402
1403 my $file = $_[0];
1404 my $cksum = &getGlobal('BIN',"cksum");
1405 if( -e $file) {
1406
1407 my ($sum,$size,$ckfile) = split(/\s+/, `$cksum $file`);
1408 my $commandRetVal = ($? >> 8); # find the command's return value
1409
1410 if($commandRetVal != 0) {
1411
1412 &B_log("ERROR","$cksum reported the following error:\n$!\n");
1413
1414 }
1415 else {
1416
1417 # new file size and sum are added to the hash
1418 $GLOBAL_SUM{$file}{filesize} = $size;
1419 $GLOBAL_SUM{$file}{sum} = $sum;
1420 &B_write_sums;
1421
1422 }
1423 } else {
1424 &B_log("ERROR","Can not save chksum for file: $file since it does not exist");
1425 }
1426}
1427
1428
1429###########################################################################
1430#
1431# &B_delete_file ($file) deletes the file $file and makes a backup to
1432# the backup directory.
1433#
1434##########################################################################
1435
1436
1437sub B_delete_file($) { #Currently Linux only (TMPDIR)
1438 #consideration: should create clear_sum routine if this is ever used to remove
1439 # A Bastille-generated file.
1440
1441 #
1442 # This API routine deletes the named file, backing it up first to the
1443 # backup directory.
1444 #
1445
1446 my $filename=shift @_;
1447 my $retval=1;
1448
1449 # We have to append the prefix ourselves since we don't use B_open_plus
1450
1451 my $original_filename=$filename;
1452
1453 &B_log("ACTION","Deleting (and backing-up) file $original_filename\n");
1454 &B_log("ACTION","rm $original_filename\n");
1455
1456 unless ($filename) {
1457 &B_log("ERROR","B_delete_file called with no arguments!\n");
1458 }
1459
1460 unless ($GLOBAL_LOGONLY) {
1461 if ( B_backup_file($original_filename) ) {
1462 unless ( unlink $filename ) {
1463 &B_log("ERROR","Couldn't unlink file $original_filename");
1464 $retval=0;
1465 }
1466 }
1467 else {
1468 $retval=0;
1469 &B_log("ERROR","B_delete_file did not delete $original_filename since it could not back it up\n");
1470 }
1471 }
1472
1473 $retval;
1474
1475}
1476
1477
1478###########################################################################
1479# &B_create_file ($file) creates the file $file, if it doesn't already
1480# exist.
1481# It will set a default mode of 0700 and a default uid/gid or 0/0.
1482#
1483# &B_create_file, to support Bastille's revert functionality, writes an
1484# rm $file command to the end of the file &getGlobal('BFILE', "created-files").
1485#
1486##########################################################################
1487
1488
1489sub B_create_file($) {
1490
1491 my $file = $_[0];
1492 my $retval=1;
1493
1494 # We have to create the file ourselves since we don't use B_open_plus
1495
1496 my $original_file = $file;
1497
1498 if ($file eq ""){
1499 &B_log("ERROR","Internal Error, attempt made to create blank filename");
1500 return 0; #False
1501 }
1502
1503 unless ( -e $file ) {
1504
1505 unless ($GLOBAL_LOGONLY) {
1506
1507 # find the directory in which the file is to reside.
1508 my $dirName = dirname($file);
1509 # if the directory does not exist then
1510 if(! -d $dirName) {
1511 # create it.
1512 mkpath ($dirName,0,0700);
1513 }
1514
1515 $retval=open CREATE_FILE,">$file";
1516
1517 if ($retval) {
1518 close CREATE_FILE;
1519 chmod 0700,$file;
1520 # Make the revert functionality
1521 &B_revert_log( &getGlobal('BIN','rm') . " $original_file \n");
1522 } else {
1523 &B_log("ERROR","Couldn't create file $original_file even though " .
1524 "it didn't already exist!\n");
1525 }
1526 }
1527 &B_log("ACTION","Created file $original_file\n");
1528 } else {
1529 &B_log("DEBUG","Didn't create file $original_file since it already existed.\n");
1530 $retval=0;
1531 }
1532
1533 $retval;
1534}
1535
1536
1537###########################################################################
1538# &B_create_dir ($dir) creates the directory $dir, if it doesn't already
1539# exist.
1540# It will set a default mode of 0700 and a default uid/gid or 0/0.
1541#
1542##########################################################################
1543
1544
1545sub B_create_dir($) {
1546
1547 my $dir = $_[0];
1548 my $retval=1;
1549
1550 # We have to append the prefix ourselves since we don't use B_open_plus
1551
1552 my $original_dir=$dir;
1553
1554 unless ( -d $dir ) {
1555 unless ($GLOBAL_LOGONLY) {
1556 $retval=mkdir $dir,0700;
1557
1558 if ($retval) {
1559 # Make the revert functionality
1560 &B_revert_log (&getGlobal('BIN','rmdir') . " $original_dir\n");
1561 }
1562 else {
1563 &B_log("ERROR","Couldn't create dir $original_dir even though it didn't already exist!");
1564 }
1565
1566 }
1567 &B_log("ACTION","Created directory $original_dir\n");
1568 }
1569 else {
1570 &B_log("ACTION","Didn't create directory $original_dir since it already existed.\n");
1571 $retval=0;
1572 }
1573
1574 $retval;
1575}
1576
1577
1578
1579###########################################################################
1580# &B_symlink ($original_file,$new_symlink) creates a symbolic link from
1581# $original_file to $new_symlink.
1582#
1583# &B_symlink respects $GLOBAL_LOGONLY. It supports
1584# the revert functionality that you've come to know and love by adding every
1585# symbolic link it creates to &getGlobal('BFILE', "created-symlinks"), currently set to:
1586#
1587# /root/Bastille/revert/revert-created-symlinks
1588#
1589# The revert script, if it works like I think it should, will run this file,
1590# which should be a script or rm's...
1591#
1592##########################################################################
1593
1594sub B_symlink($$) {
1595 my ($source_file,$new_symlink)=@_;
1596 my $retval=1;
1597 my $original_source = $source_file;
1598 my $original_symlink = $new_symlink;
1599
1600 unless ($GLOBAL_LOGONLY) {
1601 $retval=symlink $source_file,$new_symlink;
1602 if ($retval) {
1603 &B_revert_log (&getGlobal('BIN',"rm") . " $original_symlink\n");
1604 }
1605 }
1606
1607 &B_log("ACTION", "Created a symbolic link called $original_symlink from $original_source\n");
1608 &B_log("ACTION", "symlink \"$original_source\",\"$original_symlink\";\n");
1609 unless ($retval) {
1610 &B_log("ERROR","Couldn't symlink $original_symlink -> $original_source\n");
1611 }
1612
1613 $retval;
1614
1615}
1616
1617
1618sub B_cp($$) {
1619
1620 my ($source,$target)=@_;
1621 my $retval=0;
1622
1623 my $had_to_backup_target=0;
1624
1625 use File::Copy;
1626
1627 my $original_source=$source;
1628 my $original_target=$target;
1629
1630 if( -e $target and -f $target ) {
1631 &B_backup_file($original_target);
1632 &B_log("ACTION","About to copy $original_source to $original_target -- had to backup target\n");
1633 $had_to_backup_target=1;
1634 }
1635
1636 $retval=copy($source,$target);
1637 if ($retval) {
1638 &B_log("ACTION","cp $original_source $original_target\n");
1639
1640 #
1641 # We want to add a line to the &getGlobal('BFILE', "created-files") so that the
1642 # file we just put at $original_target gets deleted.
1643 #
1644 &B_revert_log(&getGlobal('BIN',"rm") . " $original_target\n");
1645 } else {
1646 &B_log("ERROR","Failed to copy $original_source to $original_target\n");
1647 }
1648 # We add the file to the GLOBAL_SUMS hash if it is not already present
1649 &B_set_sum($target);
1650 $retval;
1651}
1652
1653
1654
1655############################################################################
1656# &B_place puts a file in place, using Perl's File::cp. This file is taken
1657# from &getGlobal('BDIR', "share") and is used to place a file that came with
1658# Bastille.
1659#
1660# This should be DEPRECATED in favor of &B_cp, since the only reason it exists
1661# is because of GLOBAL_PREFIX, which has been broken for quite some time.
1662# Otherwise, the two routines are identical.
1663#
1664# It respects $GLOBAL_LOGONLY.
1665# If $target is an already-existing file, it is backed up.
1666#
1667# revert either appends another "rm $target" to &getGlobal('BFILE', "revert-actions") or
1668# backs up the file that _was_ there into the &getGlobal('BDIR', "backup"),
1669# appending a "mv" to revert-actions to put it back.
1670#
1671############################################################################
1672
1673sub B_place { # Only Linux references left (Firewall / TMPDIR)
1674
1675 my ($source,$target)=@_;
1676 my $retval=0;
1677
1678 my $had_to_backup_target=0;
1679
1680 use File::Copy;
1681
1682 my $original_source=$source;
1683 $source = &getGlobal('BDIR', "share") . $source;
1684 my $original_target=$target;
1685
1686 if ( -e $target and -f $target ) {
1687 &B_backup_file($original_target);
1688 &B_log("ACTION","About to copy $original_source to $original_target -- had to backup target\n");
1689 $had_to_backup_target=1;
1690 }
1691 $retval=copy($source,$target);
1692 if ($retval) {
1693 &B_log("ACTION","placed file $original_source as $original_target\n");
1694 #
1695 # We want to add a line to the &getGlobal('BFILE', "created-files") so that the
1696 # file we just put at $original_target gets deleted.
1697 &B_revert_log(&getGlobal('BIN',"rm") . " $original_target\n");
1698 } else {
1699 &B_log("ERROR","Failed to place $original_source as $original_target\n");
1700 }
1701
1702 # We add the file to the GLOBAL_SUMS hash if it is not already present
1703 &B_set_sum($target);
1704
1705 $retval;
1706}
1707
1708
1709
1710
1711
1712#############################################################################
1713#############################################################################
1714#############################################################################
1715
1716###########################################################################
1717# &B_mknod ($file) creates the node $file, if it doesn't already
1718# exist. It uses the prefix and suffix, like this:
1719#
1720# mknod $prefix $file $suffix
1721#
1722# This is just a wrapper to the mknod program, which tries to introduce
1723# revert functionality, by writing rm $file to the end of the
1724# file &getGlobal('BFILE', "created-files").
1725#
1726##########################################################################
1727
1728
1729sub B_mknod($$$) {
1730
1731 my ($prefix,$file,$suffix) = @_;
1732 my $retval=1;
1733
1734 # We have to create the filename ourselves since we don't use B_open_plus
1735
1736 my $original_file = $file;
1737
1738 unless ( -e $file ) {
1739 my $command = &getGlobal("BIN","mknod") . " $prefix $file $suffix";
1740
1741 if ( system($command) == 0) {
1742 # Since system will return 0 on success, invert the error code
1743 $retval=1;
1744 }
1745 else {
1746 $retval=0;
1747 }
1748
1749 if ($retval) {
1750
1751 # Make the revert functionality
1752 &B_revert_log(&getGlobal('BIN',"rm") . " $original_file\n");
1753 } else {
1754 &B_log("ERROR","Couldn't mknod $prefix $original_file $suffix even though it didn't already exist!\n");
1755 }
1756
1757
1758 &B_log("ACTION","mknod $prefix $original_file $suffix\n");
1759 }
1760 else {
1761 &B_log("ACTION","Didn't mknod $prefix $original_file $suffix since $original_file already existed.\n");
1762 $retval=0;
1763 }
1764
1765 $retval;
1766}
1767
1768###########################################################################
1769# &B_revert_log("reverse_command") prepends a command to a shell script. This
1770# shell script is intended to be run by bastille -r to reverse the changes that
1771# Bastille made, returning the files which Bastille changed to their original
1772# state.
1773###########################################################################
1774
1775sub B_revert_log($) {
1776
1777 my $revert_command = $_[0];
1778 my $revert_actions = &getGlobal('BFILE', "revert-actions");
1779 my $revertdir= &getGlobal('BDIR', "revert");
1780 my @lines;
1781
1782
1783 if (! (-e $revert_actions)) {
1784 mkpath($revertdir); #if this doesn't work next line catches
1785 if (open REVERT_ACTIONS,">" . $revert_actions){ # create revert file
1786 close REVERT_ACTIONS; # chown to root, rwx------
1787 chmod 0700,$revert_actions;
1788 chown 0,0,$revert_actions;
1789 }
1790 else {
1791 &B_log("FATAL","Can not create revert-actions file: $revert_actions.\n" .
1792 " Unable to add the following command to the revert\n" .
1793 " actions script: $revert_command\n");
1794 }
1795
1796 }
1797
1798 &B_open_plus (*REVERT_NEW, *REVERT_OLD, $revert_actions);
1799
1800 while (my $line=<REVERT_OLD>) { #copy file into @lines
1801 push (@lines,$line);
1802 }
1803 print REVERT_NEW $revert_command . "\n"; #make the revert command first in the new file
1804 while (my $line = shift @lines) { #write the rest of the lines of the file
1805 print REVERT_NEW $line;
1806 }
1807 close REVERT_OLD;
1808 close REVERT_NEW;
1809 if (rename "${revert_actions}.bastille", $revert_actions) { #replace the old file with the new file we
1810 chmod 0700,$revert_actions; # just made / mirrors B_close_plus logic
1811 chown 0,0,$revert_actions;
1812 } else {
1813 &B_log("ERROR","B_revert_log: not able to move ${revert_actions}.bastille to ${revert_actions}!!! $!) !!!\n");
1814 }
1815}
1816
1817
1818###########################################################################
1819# &getGlobalConfig($$)
1820#
1821# returns the requested GLOBAL_CONFIG hash value, ignoring the error
1822# if the value does not exist (because every module uses this to find
1823# out if the question was answered "Y")
1824###########################################################################
1825sub getGlobalConfig ($$) {
1826 my $module = $_[0];
1827 my $key = $_[1];
1828 if (exists $GLOBAL_CONFIG{$module}{$key}) {
1829 my $answer=$GLOBAL_CONFIG{$module}{$key};
1830 &B_log("ACTION","Answer to question $module.$key is \"$answer\".\n");
1831 return $answer;
1832 } else {
1833 &B_log("ACTION","Answer to question $module.$key is undefined.");
1834 return undef;
1835 }
1836}
1837
1838###########################################################################
1839# &getGlobal($$)
1840#
1841# returns the requested GLOBAL_* hash value, and logs an error
1842# if the variable does not exist.
1843###########################################################################
1844sub getGlobal ($$) {
1845 my $type = uc($_[0]);
1846 my $key = $_[1];
1847
1848 # define a mapping from the first argument to the proper hash
1849 my %map = ("BIN" => \%GLOBAL_BIN,
1850 "FILE" => \%GLOBAL_FILE,
1851 "BFILE" => \%GLOBAL_BFILE,
1852 "DIR" => \%GLOBAL_DIR,
1853 "BDIR" => \%GLOBAL_BDIR,
1854 "ERROR" => \%GLOBAL_ERROR,
1855 "SERVICE" => \%GLOBAL_SERVICE,
1856 "SERVTYPE" => \%GLOBAL_SERVTYPE,
1857 "PROCESS" => \%GLOBAL_PROCESS,
1858 "RCCONFIG" => \%GLOBAL_RC_CONFIG
1859 );
1860
1861 # check to see if the desired key is in the desired hash
1862 if (exists $map{$type}->{$key}) {
1863 # get the value from the right hash with the key
1864 return $map{$type}->{$key};
1865 } else {
1866 # i.e. Bastille tried to use $GLOBAL_BIN{'cp'} but it does not exist.
1867 # Note that we can't use B_log, since it uses getGlobal ... recursive before
1868 # configureForDistro is run.
1869 print STDERR "ERROR: Bastille tried to use \$GLOBAL_${type}\{\'$key\'} but it does not exist.\n";
1870 return undef;
1871 }
1872}
1873
1874###########################################################################
1875# &getGlobal($$)
1876#
1877# sets the requested GLOBAL_* hash value
1878###########################################################################
1879sub setGlobal ($$$) {
1880 my $type = uc($_[0]);
1881 my $key = $_[1];
1882 my $input_value = $_[2];
1883
1884 # define a mapping from the first argument to the proper hash
1885 my %map = ("BIN" => \%GLOBAL_BIN,
1886 "FILE" => \%GLOBAL_FILE,
1887 "BFILE" => \%GLOBAL_BFILE,
1888 "DIR" => \%GLOBAL_DIR,
1889 "BDIR" => \%GLOBAL_BDIR,
1890 "ERROR" => \%GLOBAL_ERROR,
1891 "SERVICE" => \%GLOBAL_SERVICE,
1892 "SERVTYPE" => \%GLOBAL_SERVTYPE,
1893 "PROCESS" => \%GLOBAL_PROCESS,
1894 );
1895
1896 if ($map{$type}->{$key} = $input_value) {
1897 return 1;
1898 } else {
1899 &B_log('ERROR','Internal Error, Unable to set global config value:' . $type . ", " .$key);
1900 return 0;
1901 }
1902}
1903
1904
1905###########################################################################
1906# &showDisclaimer:
1907# Print the disclaimer and wait for 2 minutes for acceptance
1908# Do NOT do so if any of the following conditions hold
1909# 1. the -n option was used
1910# 2. the file ~/.bastille_disclaimer exists
1911###########################################################################
1912
1913sub showDisclaimer($) {
1914
1915 my $nodisclaim = $_[0];
1916 my $nodisclaim_file = &getGlobal('BFILE', "nodisclaimer");
1917 my $response;
1918 my $WAIT_TIME = 300; # we'll wait for 5 minutes
1919 my $developersAnd;
1920 my $developersOr;
1921 if ($GLOBAL_OS =~ "^HP-UX") {
1922 $developersAnd ="HP AND ITS";
1923 $developersOr ="HP OR ITS";
1924 }else{
1925 $developersAnd ="JAY BEALE, THE BASTILLE DEVELOPERS, AND THEIR";
1926 $developersOr ="JAY BEALE, THE BASTILLE DEVELOPERS, OR THEIR";
1927 }
1928 my $DISCLAIMER =
1929 "\n" .
1930 "Copyright (C) 1999-2006 Jay Beale\n" .
1931 "Copyright (C) 1999-2001 Peter Watkins\n" .
1932 "Copyright (C) 2000 Paul L. Allen\n" .
1933 "Copyright (C) 2001-2007 Hewlett-Packard Development Company, L.P.\n" .
1934 "Bastille is free software; you are welcome to redistribute it under\n" .
1935 "certain conditions. See the \'COPYING\' file in your distribution for terms.\n\n" .
1936 "DISCLAIMER. Use of Bastille can help optimize system security, but does not\n" .
1937 "guarantee system security. Information about security obtained through use of\n" .
1938 "Bastille is provided on an AS-IS basis only and is subject to change without\n" .
1939 "notice. Customer acknowledges they are responsible for their system\'s security.\n" .
1940 "TO THE EXTENT ALLOWED BY LOCAL LAW, Bastille (\"SOFTWARE\") IS PROVIDED TO YOU \n" .
1941 "\"AS IS\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER ORAL OR WRITTEN,\n" .
1942 "EXPRESS OR IMPLIED. $developersAnd SUPPLIERS\n" .
1943 "DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE \n" .
1944 "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n" .
1945 "Some countries, states and provinces do not allow exclusions of implied\n" .
1946 "warranties or conditions, so the above exclusion may not apply to you. You may\n" .
1947 "have other rights that vary from country to country, state to state, or province\n" .
1948 "to province. EXCEPT TO THE EXTENT PROHIBITED BY LOCAL LAW, IN NO EVENT WILL\n" .
1949 "$developersOr SUBSIDIARIES, AFFILIATES OR\n" .
1950 "SUPPLIERS BE LIABLE FOR DIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR OTHER\n" .
1951 "DAMAGES (INCLUDING LOST PROFIT, LOST DATA, OR DOWNTIME COSTS), ARISING OUT OF\n" .
1952 "THE USE, INABILITY TO USE, OR THE RESULTS OF USE OF THE SOFTWARE, WHETHER BASED\n" .
1953 "IN WARRANTY, CONTRACT, TORT OR OTHER LEGAL THEORY, AND WHETHER OR NOT ADVISED\n" .
1954 "OF THE POSSIBILITY OF SUCH DAMAGES. Your use of the Software is entirely at your\n" .
1955 "own risk. Should the Software prove defective, you assume the entire cost of all\n" .
1956 "service, repair or correction. Some countries, states and provinces do not allow\n" .
1957 "the exclusion or limitation of liability for incidental or consequential \n" .
1958 "damages, so the above limitation may not apply to you. This notice will only \n".
1959 "display on the first run on a given system.\n".
1960 "To suppress the disclaimer on other machines, use Bastille\'s -n flag (example: bastille -n).\n";
1961
1962
1963# If the user has specified not to show the disclaimer, or
1964# the .bastille_disclaimer file already exists, then return
1965 if( ( $nodisclaim ) || -e $nodisclaim_file ) { return 1; }
1966
1967# otherwise, show the disclaimer
1968 print ($DISCLAIMER);
1969
1970# there is a response
1971 my $touch = &getGlobal('BIN', "touch");
1972 my $retVal = system("$touch $nodisclaim_file");
1973 if( $retVal != 0 ) {
1974 &ErrorLog ( &getGlobal('ERROR','disclaimer'));
1975 }
1976} # showDisclaimer
1977
1978
1979
1980
1981################################################################
1982# &systemCall
1983#Function used by exported methods B_Backtick and B_system
1984#to handle the mechanics of system calls.
1985# This function also manages error handling.
1986# Input: a system call
1987# Output: a list containing the status, sstdout and stderr
1988# of the the system call
1989#
1990################################################################
1991sub systemCall ($){
1992 no strict;
1993 local $command=$_[0]; # changed scoping so eval below can read it
1994
1995 local $SIG{'ALRM'} = sub { die "timeout" }; # This subroutine exits the "eval" below. The program
1996 # can then move on to the next operation. Used "local"
1997 # to avoid name space collision with disclaim alarm.
1998 local $WAIT_TIME=120; # Wait X seconds for system commands
1999 local $commandOutput = '';
2000 my $errOutput = '';
2001 eval{
2002 $errorFile = &getGlobal('BFILE','stderrfile');
2003 unlink($errorFile); #To make sure we don't mix output
2004 alarm($WAIT_TIME); # start a time-out for command to complete. Some commands hang, and we want to
2005 # fail gracefully. When we call "die" it exits this eval statement
2006 # with a value we use below
2007 $commandOutput = `$command 2> $errorFile`; # run the command and gather its output
2008 my $commandRetVal = ($? >> 8); # find the commands return value
2009 if ($commandRetVal == 0) {
2010 &B_log("ACTION","Executed Command: " . $command . "\n");
2011 &B_log("ACTION","Command Output: " . $commandOutput . "\n");
2012 die "success";
2013 } else {
2014 die "failure";
2015 };
2016 };
2017
2018 my $exitcode=$@;
2019 alarm(0); # End of the timed operation
2020
2021 my $cat = &getGlobal("BIN","cat");
2022 if ( -e $errorFile ) {
2023 $errOutput = `$cat $errorFile`;
2024 }
2025
2026 if ($exitcode) { # The eval command above will exit with one of the 3 values below
2027 if ($exitcode =~ /timeout/) {
2028 &B_log("WARNING","No response received from $command after $WAIT_TIME seconds.\n" .
2029 "Command Output: " . $commandOutput . "\n");
2030 return (0,'','');
2031 } elsif ($exitcode =~ /success/) {
2032 return (1,$commandOutput,$errOutput);
2033 } elsif ($exitcode =~ /failure/) {
2034 return (0,$commandOutput,$errOutput);
2035 } else {
2036 &B_log("FATAL","Unexpected return state from command execution: $command\n" .
2037 "Command Output: " . $commandOutput . "\n");
2038 }
2039 }
2040}
2041
2042#############################################
2043# Use this **only** for commands used that are
2044# intended to test system state and
2045# not make any system change. Use this in place of the
2046# prior use of "backticks throughout Bastille
2047# Handles basic output redirection, but not for stdin
2048# Input: Command
2049# Output: Results
2050#############################################
2051
2052sub B_Backtick($) {
2053 my $command=$_[0];
2054 my $combineOutput=0;
2055 my $stdoutRedir = "";
2056 my $stderrRedir = "";
2057 my $echo = &getGlobal('BIN','echo');
2058
2059 if (($command =~ s/2>&1//) or
2060 (s/>&2//)){
2061 $combineOutput=1;
2062 }
2063 if ($command =~ s/>\s*([^>\s])+// ) {
2064 $stdoutRedir = $1;
2065 }
2066 if ($command =~ s/2>\s*([^>\s])+// ) {
2067 $stderrRedir = $1;
2068 }
2069
2070 my ($ranFine, $stdout, $stderr) = &systemCall($command);
2071 if ($ranFine) {
2072 &B_log("DEBUG","Command: $command succeeded for test with output: $stdout , ".
2073 "and stderr: $stderr");
2074 } else {
2075 &B_log("DEBUG","Command: $command failed for test with output: $stdout , ".
2076 "and stderr: $stderr");
2077 }
2078 if ($combineOutput) {
2079 $stdout .= $stderr;
2080 $stderr = $stdout; #these should be the same
2081 }
2082 if ($stdoutRedir ne "") {
2083 system("$echo \'$stdout\' > $stdoutRedir");
2084 }
2085 if ($stderrRedir ne "") {
2086 system("$echo \'$stderr\' > $stderrRedir");
2087 }
2088 return $stdout;
2089}
2090
2091####################################################################
2092# &B_System($command,$revertcommand);
2093# This function executes a command, then places the associated
2094# revert command in revert file. It takes two parameters, the
2095# command and the command that reverts that command.
2096#
2097# uses ActionLog and ErrorLog for logging purposes.
2098###################################################################
2099sub B_System ($$) {
2100 my ($command,$revertcmd)=@_;
2101
2102 my ($ranFine, $stdout, $stderr) = &systemCall($command);
2103 if ($ranFine) {
2104 &B_revert_log ("$revertcmd \n");
2105 if ($stderr ne '' ) {
2106 &B_log("ACTION",$command . "suceeded with STDERR: " .
2107 $stderr . "\n");
2108 }
2109 return 1;
2110 } else {
2111 my $warningString = "Command Failed: " . $command . "\n" .
2112 "Command Output: " . $stdout . "\n";
2113 if ($stderr ne '') {
2114 $warningString .= "Error message: " . $stderr;
2115 }
2116 &B_log("WARNING", $warningString);
2117 return 0;
2118 }
2119}
2120
2121
2122###########################################################################
2123# &isProcessRunning($procPattern);
2124#
2125# If called in scalar context this subroutine will return a 1 if the
2126# pattern specified can be matched against the process table. It will
2127# return a 0 otherwise.
2128# If called in the list context this subroutine will return the list
2129# of processes which matched the pattern supplied
2130#
2131# scalar return values:
2132# 0: pattern not in process table
2133# 1: pattern is in process table
2134#
2135# list return values:
2136# proc lines from the process table if they are found
2137###########################################################################
2138sub isProcessRunning($) {
2139
2140 my $procPattern= $_[0];
2141 my $ps = &getGlobal('BIN',"ps");
2142
2143 my $isRunning=0;
2144 # process table.
2145 my @psTable = `$ps -elf`;
2146 # list of processes that match the $procPattern
2147 my @procList;
2148 foreach my $process (@psTable) {
2149 if($process =~ $procPattern) {
2150 $isRunning = 1;
2151 push @procList, $process . "\n";
2152 }
2153 }
2154
2155 &B_log("DEBUG","$procPattern search yielded $isRunning\n\n");
2156 # if this subroutine was called in scalar context
2157 if( ! wantarray ) {
2158 return $isRunning;
2159 }
2160
2161 return @procList;
2162}
2163
2164
2165###########################################################################
2166# &checkProcsForService($service);
2167#
2168# Checks if the given service is running by analyzing the process table.
2169# This is a helper function to checkServiceOnLinux and checkServiceOnHP
2170#
2171# Return values:
2172# SECURE_CANT_CHANGE() if the service is off
2173# INCONSISTENT() if the state of the service cannot be determined
2174#
2175# Mostly used in "check service" direct-return context, but added option use.
2176# to ignore warning if a check for a service ... where a found service doesn't
2177# have direct security problems.
2178#
2179###########################################################################
2180sub checkProcsForService ($;$) {
2181 my $service=$_[0];
2182 my $ignore_warning=$_[1];
2183
2184 my @psnames=@{ &getGlobal('PROCESS',$service)};
2185
2186 my @processes;
2187 # inetd services don't have a separate process
2188 foreach my $psname (@psnames) {
2189 my @procList = &isProcessRunning($psname);
2190 if(@procList >= 0){
2191 splice @processes,$#processes+1,0,@procList;
2192 }
2193 }
2194
2195 if($#processes >= 0){
2196 if ((defined($ignore_warning)) and ($ignore_warning eq "ignore_warning")) {
2197 &B_log("WARNING","The following processes were still running even though " .
2198 "the corresponding service appears to be turned off. Bastille " .
2199 "question and action will be skipped.\n\n" .
2200 "@processes\n\n");
2201 # processes were still running, service is not off, but we don't know how
2202 # to configure it so we skip the question
2203 return INCONSISTENT();
2204 } else {
2205 return NOTSECURE_CAN_CHANGE(); # In the case we're ignoring the warning,
2206 # ie: checking to make *sure* a process
2207 # is running, the answer isn't inconsistent
2208 }
2209 } else {
2210 &B_log("DEBUG","$service is off. Found no processes running on the system.");
2211 # no processes, so service is off
2212 return SECURE_CANT_CHANGE();
2213 }
2214 # Can't determine the state of the service by looking at the processes,
2215 # so return INCONSISTENT().
2216 return INCONSISTENT();
2217}
2218
2219###########################################################################
2220# B_parse_fstab()
2221#
2222# Search the filesystem table for a specific mount point.
2223#
2224# scalar return value:
2225# The line form the table that matched the mount point, or the null string
2226# if no match was found.
2227#
2228# list return value:
2229# A list of parsed values from the line of the table that matched, with
2230# element [3] containing a reference to a hash of the mount options. The
2231# keys are: acl, dev, exec, rw, suid, sync, or user. The value of each key
2232# can be either 0 or 1. To access the hash, use code similar to this:
2233# %HashResult = %{(&B_parse_fstab($MountPoint))[3]};
2234#
2235###########################################################################
2236
2237sub B_parse_fstab($)
2238{
2239 my $name = shift;
2240 my $file = &getGlobal('FILE','fstab');
2241 my ($enable, $disable, $infile);
2242 my @lineopt;
2243 my $retline = "";
2244 my @retlist = ();
2245
2246 unless (open FH, $file) {
2247 &B_log('ERROR',"B_parse_fstab couldn't open fstab file at path $file.\n");
2248 return 0;
2249 }
2250 while (<FH>) {
2251 s/\#.*//;
2252 next unless /\S/;
2253 @retlist = split;
2254 next unless $retlist[1] eq $name;
2255 $retline .= $_;
2256 if (wantarray) {
2257 my $option = { # initialize to defaults
2258 acl => 0, # for ext2, etx3, reiserfs
2259 dev => 1,
2260 exec => 1,
2261 rw => 1,
2262 suid => 1,
2263 sync => 0,
2264 user => 0,
2265 };
2266
2267 my @lineopt = split(',',$retlist[3]);
2268 foreach my $entry (@lineopt) {
2269 if ($entry eq 'acl') {
2270 $option->{'acl'} = 1;
2271 }
2272 elsif ($entry eq 'nodev') {
2273 $option->{'dev'} = 0;
2274 }
2275 elsif ($entry eq 'noexec') {
2276 $option->{'exec'} = 0;
2277 }
2278 elsif ($entry eq 'ro') {
2279 $option->{'rw'} = 0;
2280 }
2281 elsif ($entry eq 'nosuid') {
2282 $option->{'suid'} = 0;
2283 }
2284 elsif ($entry eq 'sync') {
2285 $option->{'sync'} = 1;
2286 }
2287 elsif ($entry eq 'user') {
2288 $option->{'user'} = 1;
2289 }
2290 }
2291 $retlist[3]= $option;
2292 }
2293 last;
2294 }
2295
2296 if (wantarray)
2297 {
2298 return @retlist;
2299 }
2300 else
2301 {
2302 return $retline;
2303 }
2304
2305}
2306
2307
2308###########################################################################
2309# B_parse_mtab()
2310#
2311# This routine returns a hash of devices and their mount points from mtab,
2312# simply so you can get a list of mounted filesystems.
2313#
2314###########################################################################
2315
2316sub B_parse_mtab
2317{
2318 my $mountpoints;
2319 open(MTAB,&getGlobal('FILE','mtab'));
2320 while(my $mtab_line = <MTAB>) {
2321 #test if it's a device
2322 if ($mtab_line =~ /^\//)
2323 {
2324 #parse out device and mount point
2325 $mtab_line =~ /^(\S+)\s+(\S+)/;
2326 $mountpoints->{$1} = $2;
2327 }
2328 }
2329 return $mountpoints;
2330}
2331
2332
2333###########################################################################
2334# B_is_rpm_up_to_date()
2335#
2336#
2337###########################################################################
2338
2339sub B_is_rpm_up_to_date(@)
2340{
2341 my($nameB,$verB,$relB,$epochB) = @_;
2342 my $installedpkg = $nameB;
2343
2344 if ($epochB =~ /(none)/) {
2345 $epochB = 0;
2346 }
2347
2348 my $rpmA = `rpm -q --qf '%{VERSION}-%{RELEASE}-%{EPOCH}\n' $installedpkg`;
2349 my $nameA = $nameB;
2350 my ($verA,$relA,$epochA);
2351
2352 my $retval;
2353
2354 # First, if the RPM isn't installed, let's handle that.
2355 if ($rpmA =~ /is not installed/) {
2356 $retval = -1;
2357 return $retval;
2358 }
2359 else {
2360 # Next, let's try to parse the EVR information without as few
2361 # calls as possible to rpm.
2362 if ($rpmA =~ /([^-]+)-([^-]+)-([^-]+)$/) {
2363 $verA = $1;
2364 $relA = $2;
2365 $epochA = $3;
2366 }
2367 else {
2368 $nameA = `rpm -q --qf '%{NAME}' $installedpkg`;
2369 $verA = `rpm -q --qf '%{VERSION}' $installedpkg`;
2370 $relA = `rpm -q --qf '%{RELEASE}' $installedpkg`;
2371 $epochA = `rpm -q --qf '%{EPOCH}' $installedpkg`;
2372 }
2373 }
2374
2375 # Parse "none" as 0.
2376 if ($epochA =~ /(none)/) {
2377 $epochA = 0;
2378 }
2379
2380 # Handle the case where only one of them is zero.
2381 if ($epochA == 0 xor $epochB == 0)
2382 {
2383 if ($epochA != 0)
2384 {
2385 $retval = 1;
2386 }
2387 else
2388 {
2389 $retval = 0;
2390 }
2391 }
2392 else
2393 {
2394 # ...otherwise they are either both 0 or both non-zero and
2395 # so the situation isn't trivial.
2396
2397 # Check epoch first - highest epoch wins.
2398 my $rpmcmp = &cmp_vers_part($epochA, $epochB);
2399 #print "epoch rpmcmp is $rpmcmp\n";
2400 if ($rpmcmp > 0)
2401 {
2402 $retval = 1;
2403 }
2404 elsif ($rpmcmp < 0)
2405 {
2406 $retval = 0;
2407 }
2408 else
2409 {
2410 # Epochs were the same. Check Version now.
2411 $rpmcmp = &cmp_vers_part($verA, $verB);
2412 #print "epoch rpmcmp is $rpmcmp\n";
2413 if ($rpmcmp > 0)
2414 {
2415 $retval = 1;
2416 }
2417 elsif ($rpmcmp < 0)
2418 {
2419 $retval = 0;
2420 }
2421 else
2422 {
2423 # Versions were the same. Check Release now.
2424 my $rpmcmp = &cmp_vers_part($relA, $relB);
2425 #print "epoch rpmcmp is $rpmcmp\n";
2426 if ($rpmcmp >= 0)
2427 {
2428 $retval = 1;
2429 }
2430 elsif ($rpmcmp < 0)
2431 {
2432 $retval = 0;
2433 }
2434 }
2435 }
2436 }
2437 return $retval;
2438}
2439
2440#################################################
2441# Helper function for B_is_rpm_up_to_date()
2442#################################################
2443
2444#This cmp_vers_part function taken from Kirk Bauer's Autorpm.
2445# This version comparison code was sent in by Robert Mitchell and, although
2446# not yet perfect, is better than the original one I had. He took the code
2447# from freshrpms and did some mods to it. Further mods by Simon Liddington
2448# <sjl96v@ecs.soton.ac.uk>.
2449#
2450# Splits string into minors on . and change from numeric to non-numeric
2451# characters. Minors are compared from the beginning of the string. If the
2452# minors are both numeric then they are numerically compared. If both minors
2453# are non-numeric and a single character they are alphabetically compared, if
2454# they are not a single character they are checked to be the same if the are not
2455# the result is unknown (currently we say the first is newer so that we have
2456# a choice to upgrade). If one minor is numeric and one non-numeric then the
2457# numeric one is newer as it has a longer version string.
2458# We also assume that (for example) .15 is equivalent to 0.15
2459
2460sub cmp_vers_part($$) {
2461 my($va, $vb) = @_;
2462 my(@va_dots, @vb_dots);
2463 my($a, $b);
2464 my($i);
2465
2466 if ($vb !~ /^pre/ and $va =~ s/^pre(\d+.*)$/$1/) {
2467 if ($va eq $vb) { return -1; }
2468 } elsif ($va !~ /^pre/ and $vb =~ s/^pre(\d+.*)$/$1/) {
2469 if ($va eq $vb) { return 1; }
2470 }
2471
2472 @va_dots = split(/\./, $va);
2473 @vb_dots = split(/\./, $vb);
2474
2475 $a = shift(@va_dots);
2476 $b = shift(@vb_dots);
2477 # We also assume that (for example) .15 is equivalent to 0.15
2478 if ($a eq '' && $va ne '') { $a = "0"; }
2479 if ($b eq '' && $vb ne '') { $b = "0"; }
2480 while ((defined($a) && $a ne '') || (defined($b) && $b ne '')) {
2481 # compare each minor from left to right
2482 if ((not defined($a)) || ($a eq '')) { return -1; } # the longer version is newer
2483 if ((not defined($b)) || ($b eq '')) { return 1; }
2484 if ($a =~ /^\d+$/ && $b =~ /^\d+$/) {
2485 # I have changed this so that when the two strings are numeric, but one or both
2486 # of them start with a 0, then do a string compare - Kirk Bauer - 5/28/99
2487 if ($a =~ /^0/ or $b =~ /^0/) {
2488 # We better string-compare so that netscape-4.6 is newer than netscape-4.08
2489 if ($a ne $b) {return ($a cmp $b);}
2490 }
2491 # numeric compare
2492 if ($a != $b) { return $a <=> $b; }
2493 } elsif ($a =~ /^\D+$/ && $b =~ /^\D+$/) {
2494 # string compare
2495 if (length($a) == 1 && length($b) == 1) {
2496 # only minors with one letter seem to be useful for versioning
2497 if ($a ne $b) { return $a cmp $b; }
2498 } elsif (($a cmp $b) != 0) {
2499 # otherwise we should at least check they are the same and if not say unknown
2500 # say newer for now so at least we get choice whether to upgrade or not
2501 return -1;
2502 }
2503 } elsif ( ($a =~ /^\D+$/ && $b =~ /^\d+$/) || ($a =~ /^\d+$/ && $b =~ /^\D+$/) ) {
2504 # if we get a number in one and a word in another the one with a number
2505 # has a longer version string
2506 if ($a =~ /^\d+$/) { return 1; }
2507 if ($b =~ /^\d+$/) { return -1; }
2508 } else {
2509 # minor needs splitting
2510 $a =~ /\d+/ || $a =~ /\D+/;
2511 # split the $a minor into numbers and non-numbers
2512 my @va_bits = ($`, $&, $');
2513 $b =~ /\d+/ || $b =~ /\D+/;
2514 # split the $b minor into numbers and non-numbers
2515 my @vb_bits = ($`, $&, $');
2516 for ( my $j=2; $j >= 0; $j--) {
2517 if ($va_bits[$j] ne '') { unshift(@va_dots,$va_bits[$j]); }
2518 if ($vb_bits[$j] ne '') { unshift(@vb_dots,$vb_bits[$j]); }
2519 }
2520 }
2521 $a = shift(@va_dots);
2522 $b = shift(@vb_dots);
2523 }
2524 return 0;
2525}
2526
25271;
2528
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 @@
1package Bastille::API::AccountPermission;
2use strict;
3
4use Bastille::API;
5
6use Bastille::API::HPSpecific;
7
8require Exporter;
9our @ISA = qw(Exporter);
10our @EXPORT_OK = qw(
11B_chmod
12B_chmod_if_exists
13B_chown
14B_chown_link
15B_chgrp
16B_chgrp_link
17B_userdel
18B_groupdel
19B_remove_user_from_group
20B_check_owner_group
21B_is_unowned_file
22B_is_ungrouped_file
23B_check_permissions
24B_permission_test
25B_find_homes
26B_is_executable
27B_is_suid
28B_is_sgid
29B_get_user_list
30B_get_group_list
31B_remove_suid
32);
33our @EXPORT = @EXPORT_OK;
34
35###########################################################################
36# &B_chmod ($mode, $file) sets the mode of $file to $mode. $mode must
37# be stored in octal, so if you want to give mode 700 to /etc/aliases,
38# you need to use:
39#
40# &B_chmod ( 0700 , "/etc/aliases");
41#
42# where the 0700 denotes "octal 7-0-0".
43#
44# &B_chmod ($mode_changes,$file) also respects the symbolic methods of
45# changing file permissions, which are often what question authors are
46# really seeking.
47#
48# &B_chmod ("u-s" , "/bin/mount")
49# or
50# &B_chmod ("go-rwx", "/bin/mount")
51#
52#
53# &B_chmod respects GLOBAL_LOGONLY and uses
54# &B_revert_log used to insert a shell command that will return
55# the permissions to the pre-Bastille state.
56#
57# B_chmod allow for globbing now, as of 1.2.0. JJB
58#
59##########################################################################
60
61
62sub B_chmod($$) {
63 my ($new_perm,$file_expr)=@_;
64 my $old_perm;
65 my $old_perm_raw;
66 my $new_perm_formatted;
67 my $old_perm_formatted;
68
69 my $retval=1;
70
71 my $symbolic = 0;
72 my ($chmod_noun,$add_remove,$capability) = ();
73 # Handle symbolic possibilities too
74 if ($new_perm =~ /([ugo]+)([+-]{1})([rwxst]+)/) {
75 $symbolic = 1;
76 $chmod_noun = $1;
77 $add_remove = $2;
78 $capability = $3;
79 }
80
81 my $file;
82 my @files = glob ($file_expr);
83
84 foreach $file (@files) {
85
86 # Prepend global prefix, but save the original filename for B_backup_file
87 my $original_file=$file;
88
89 # Store the old permissions so that we can log them.
90 unless (stat $file) {
91 &B_log("ERROR","Couldn't stat $original_file from $old_perm to change permissions\n");
92 next;
93 }
94
95 $old_perm_raw=(stat(_))[2];
96 $old_perm= (($old_perm_raw/512) % 8) .
97 (($old_perm_raw/64) % 8) .
98 (($old_perm_raw/8) % 8) .
99 ($old_perm_raw % 8);
100
101 # If we've gone symbolic, calculate the new permissions in octal.
102 if ($symbolic) {
103 #
104 # We calculate the new permissions by applying a bitmask to
105 # the current permissions, by OR-ing (for +) or XOR-ing (for -).
106 #
107 # We create this mask by first calculating a perm_mask that forms
108 # the right side of this, then multiplying it by 8 raised to the
109 # appropriate power to affect the correct digit of the octal mask.
110 # This means that we raise 8 to the power of 0,1,2, or 3, based on
111 # the noun of "other","group","user", or "suid/sgid/sticky".
112 #
113 # Actually, we handle multiple nouns by summing powers of 8.
114 #
115 # The only tough part is that we have to handle suid/sgid/sticky
116 # differently.
117 #
118
119 # We're going to calculate a mask to OR or XOR with the current
120 # file mode. This mask is $mask. We calculate this by calculating
121 # a sum of powers of 8, corresponding to user/group/other,
122 # multiplied with a $premask. The $premask is simply the
123 # corresponding bitwise expression of the rwx bits.
124 #
125 # To handle SUID, SGID or sticky in the simplest way possible, we
126 # simply add their values to the $mask first.
127
128 my $perm_mask = 00;
129 my $mask = 00;
130
131 # Check for SUID, SGID or sticky as these are exceptional.
132 if ($capability =~ /s/) {
133 if ($chmod_noun =~ /u/) {
134 $mask += 04000;
135 }
136 if ($chmod_noun =~ /g/) {
137 $mask += 02000;
138 }
139 }
140 if ($capability =~ /t/) {
141 $mask += 01000;
142 }
143
144 # Now handle the normal attributes
145 if ($capability =~ /[rwx]/) {
146 if ($capability =~ /r/) {
147 $perm_mask |= 04;
148 }
149 if ($capability =~ /w/) {
150 $perm_mask |= 02;
151 }
152 if ($capability =~ /x/) {
153 $perm_mask |= 01;
154 }
155
156 # Now figure out which 3 bit octal digit we're affecting.
157 my $power = 0;
158 if ($chmod_noun =~ /u/) {
159 $mask += $perm_mask * 64;
160 }
161 if ($chmod_noun =~ /g/) {
162 $mask += $perm_mask * 8;
163 }
164 if ($chmod_noun =~ /o/) {
165 $mask += $perm_mask * 1;
166 }
167 }
168 # Now apply the mask to get the new permissions
169 if ($add_remove eq '+') {
170 $new_perm = $old_perm_raw | $mask;
171 }
172 elsif ($add_remove eq '-') {
173 $new_perm = $old_perm_raw & ( ~($mask) );
174 }
175 }
176
177 # formating for simple long octal output of the permissions in string form
178 $new_perm_formatted=sprintf "%5lo",$new_perm;
179 $old_perm_formatted=sprintf "%5lo",$old_perm_raw;
180
181 &B_log("ACTION","change permissions on $original_file from $old_perm_formatted to $new_perm_formatted\n");
182
183 &B_log("ACTION", "chmod $new_perm_formatted,\"$original_file\";\n");
184
185 # Change the permissions on the file
186
187 if ( -e $file ) {
188 unless ($GLOBAL_LOGONLY) {
189 $retval=chmod $new_perm,$file;
190 if($retval){
191 # if the distribution is HP-UX then the modifications should
192 # also be made to the IPD (installed product database)
193 if(&GetDistro =~ "^HP-UX"){
194 &B_swmodify($file);
195 }
196 # making changes revert-able
197 &B_revert_log(&getGlobal('BIN', "chmod") . " $old_perm $file\n");
198 }
199 }
200 unless ($retval) {
201 &B_log("ERROR","Couldn't change permissions on $original_file from $old_perm_formatted to $new_perm_formatted\n");
202 $retval=0;
203 }
204 }
205 else {
206 &B_log("ERROR", "chmod: File $original_file doesn't exist!\n");
207 $retval=0;
208 }
209 }
210
211 $retval;
212
213}
214
215###########################################################################
216# &B_chmod_if_exists ($mode, $file) sets the mode of $file to $mode *if*
217# $file exists. $mode must be stored in octal, so if you want to give
218# mode 700 to /etc/aliases, you need to use:
219#
220# &B_chmod_if_exists ( 0700 , "/etc/aliases");
221#
222# where the 0700 denotes "octal 7-0-0".
223#
224# &B_chmod_if_exists respects GLOBAL_LOGONLY and uses
225# &B_revert_log to reset the permissions of the file.
226#
227# B_chmod_if_exists allow for globbing now, as of 1.2.0. JJB
228#
229##########################################################################
230
231
232sub B_chmod_if_exists($$) {
233 my ($new_perm,$file_expr)=@_;
234 # If $file_expr has a glob character, pass it on (B_chmod won't complain
235 # about nonexistent files if given a glob pattern)
236 if ( $file_expr =~ /[\*\[\{]/ ) { # } just to match open brace for vi
237 &B_log("ACTION","Running chmod $new_perm $file_expr");
238 return(&B_chmod($new_perm,$file_expr));
239 }
240 # otherwise, test for file existence
241 if ( -e $file_expr ) {
242 &B_log("ACTION","File exists, running chmod $new_perm $file_expr");
243 return(&B_chmod($new_perm,$file_expr));
244 }
245}
246
247###########################################################################
248# &B_chown ($uid, $file) sets the owner of $file to $uid, like this:
249#
250# &B_chown ( 0 , "/etc/aliases");
251#
252# &B_chown respects $GLOBAL_LOGONLY and uses
253# &B_revert_log to insert a shell command that will return
254# the file/directory owner to the pre-Bastille state.
255#
256# Unlike Perl, we've broken the chown function into B_chown/B_chgrp to
257# make error checking simpler.
258#
259# As of 1.2.0, this now supports file globbing. JJB
260#
261##########################################################################
262
263
264sub B_chown($$) {
265 my ($newown,$file_expr)=@_;
266 my $oldown;
267 my $oldgown;
268
269 my $retval=1;
270
271 my $file;
272 my @files = glob($file_expr);
273
274 foreach $file (@files) {
275
276 # Prepend prefix, but save original filename
277 my $original_file=$file;
278
279 $oldown=(stat $file)[4];
280 $oldgown=(stat $file)[5];
281
282 &B_log("ACTION","change ownership on $original_file from $oldown to $newown\n");
283 &B_log("ACTION","chown $newown,$oldgown,\"$original_file\";\n");
284 if ( -e $file ) {
285 unless ($GLOBAL_LOGONLY) {
286 # changing the files owner using perl chown function
287 $retval = chown $newown,$oldgown,$file;
288 if($retval){
289 # if the distribution is HP-UX then the modifications should
290 # also be made to the IPD (installed product database)
291 if(&GetDistro =~ "^HP-UX"){
292 &B_swmodify($file);
293 }
294 # making ownership change revert-able
295 &B_revert_log(&getGlobal('BIN', "chown") . " $oldown $file\n");
296 }
297 }
298 unless ($retval) {
299 &B_log("ERROR","Couldn't change ownership to $newown on file $original_file\n");
300 }
301 }
302 else {
303 &B_log("ERROR","chown: File $original_file doesn't exist!\n");
304 $retval=0;
305 }
306 }
307
308 $retval;
309}
310
311###########################################################################
312# &B_chown_link just like &B_chown but one exception:
313# if the input file is a link it will not change the target's ownship, it only change the link itself's ownship
314###########################################################################
315sub B_chown_link($$){
316 my ($newown,$file_expr)=@_;
317 my $chown = &getGlobal("BIN","chown");
318 my @files = glob($file_expr);
319 my $retval = 1;
320
321 foreach my $file (@files) {
322 # Prepend prefix, but save original filename
323 my $original_file=$file;
324 my $oldown=(stat $file)[4];
325 my $oldgown=(stat $file)[5];
326
327 &B_log("ACTION","change ownership on $original_file from $oldown to $newown\n");
328 &B_log("ACTION","chown -h $newown,\"$original_file\";\n");
329 if ( -e $file ) {
330 unless ($GLOBAL_LOGONLY) {
331 `$chown -h $newown $file`;
332 $retval = ($? >> 8);
333 if($retval == 0 ){
334 # if the distribution is HP-UX then the modifications should
335 # also be made to the IPD (installed product database)
336 if(&GetDistro =~ "^HP-UX"){
337 &B_swmodify($file);
338 }
339 # making ownership change revert-able
340 &B_revert_log("$chown -h $oldown $file\n");
341 }
342 }
343 unless ( ! $retval) {
344 &B_log("ERROR","Couldn't change ownership to $newown on file $original_file\n");
345 }
346 }
347 else {
348 &B_log("ERROR","chown: File $original_file doesn't exist!\n");
349 $retval=0;
350 }
351 }
352}
353
354
355###########################################################################
356# &B_chgrp ($gid, $file) sets the group owner of $file to $gid, like this:
357#
358# &B_chgrp ( 0 , "/etc/aliases");
359#
360# &B_chgrp respects $GLOBAL_LOGONLY and uses
361# &B_revert_log to insert a shell command that will return
362# the file/directory group to the pre-Bastille state.
363#
364# Unlike Perl, we've broken the chown function into B_chown/B_chgrp to
365# make error checking simpler.
366#
367# As of 1.2.0, this now supports file globbing. JJB
368#
369##########################################################################
370
371
372sub B_chgrp($$) {
373 my ($newgown,$file_expr)=@_;
374 my $oldown;
375 my $oldgown;
376
377 my $retval=1;
378
379 my $file;
380 my @files = glob($file_expr);
381
382 foreach $file (@files) {
383
384 # Prepend global prefix, but save original filename for &B_backup_file
385 my $original_file=$file;
386
387 $oldown=(stat $file)[4];
388 $oldgown=(stat $file)[5];
389
390 &B_log("ACTION", "Change group ownership on $original_file from $oldgown to $newgown\n");
391 &B_log("ACTION", "chown $oldown,$newgown,\"$original_file\";\n");
392 if ( -e $file ) {
393 unless ($GLOBAL_LOGONLY) {
394 # changing the group for the file/directory
395 $retval = chown $oldown,$newgown,$file;
396 if($retval){
397 # if the distribution is HP-UX then the modifications should
398 # also be made to the IPD (installed product database)
399 if(&GetDistro =~ "^HP-UX"){
400 &B_swmodify($file);
401 }
402 &B_revert_log(&getGlobal('BIN', "chgrp") . " $oldgown $file\n");
403 }
404 }
405 unless ($retval) {
406 &B_log("ERROR","Couldn't change ownership to $newgown on file $original_file\n");
407 }
408 }
409 else {
410 &B_log("ERROR","chgrp: File $original_file doesn't exist!\n");
411 $retval=0;
412 }
413 }
414
415 $retval;
416}
417
418###########################################################################
419# &B_chgrp_link just like &B_chgrp but one exception:
420# if the input file is a link
421# it will not change the target's ownship, it only change the link itself's ownship
422###########################################################################
423sub B_chgrp_link($$) {
424 my ($newgown,$file_expr)=@_;
425 my $chgrp = &getGlobal("BIN","chgrp");
426 my @files = glob($file_expr);
427 my $retval=1;
428
429 foreach my $file (@files) {
430 # Prepend prefix, but save original filename
431 my $original_file=$file;
432 my $oldgown=(stat $file)[5];
433
434 &B_log("ACTION","change group ownership on $original_file from $oldgown to $newgown\n");
435 &B_log("ACTION","chgrp -h $newgown \"$original_file\";\n");
436 if ( -e $file ) {
437 unless ($GLOBAL_LOGONLY) {
438 # do not follow link with option -h
439 `$chgrp -h $newgown $file`;
440 $retval = ($? >> 8);
441 if($retval == 0 ){
442 # if the distribution is HP-UX then the modifications should
443 # also be made to the IPD (installed product database)
444 if(&GetDistro =~ "^HP-UX"){
445 &B_swmodify($file);
446 }
447 # making ownership change revert-able
448 &B_revert_log("$chgrp" . " -h $oldgown $file\n");
449 }
450 }
451 unless (! $retval) {
452 &B_log("ERROR","Couldn't change group ownership to $newgown on file $original_file\n");
453 }
454 }
455 else {
456 &B_log("ERROR","chgrp: File $original_file doesn't exist!\n");
457 $retval=0;
458 }
459 }
460}
461
462###########################################################################
463# B_userdel($user) removes $user from the system, chmoding her home
464# directory to 000, root:root owned, and removes the user from all
465# /etc/passwd, /etc/shadow and /etc/group lines.
466#
467# In the future, we may also choose to make a B_lock_account routine.
468#
469# This routine depends on B_remove_user_from_group.
470###########################################################################
471
472sub B_userdel($) {
473
474 my $user_to_remove = $_[0];
475
476 if (&GetDistro =~ /^HP-UX/) {
477 return 0;
478
479 # Not yet suported on HP-UX, where we'd need to support
480 # the TCB files and such.
481 }
482
483 #
484 # First, let's chmod/chown/chgrp the user's home directory.
485 #
486
487 # Get the user's home directory from /etc/passwd
488 if (open PASSWD,&getGlobal('FILE','passwd')) {
489 my @lines=<PASSWD>;
490 close PASSWD;
491
492 # Get the home directory
493 my $user_line = grep '^\s*$user_to_remove\s*:',@lines;
494 my $home_directory = (split /\s*:\s*/,$user_line)[5];
495
496 # Chmod that home dir to 0000,owned by uid 0, gid 0.
497 if (&B_chmod_if_exists(0000,$home_directory)) {
498 &B_chown(0,$home_directory);
499 &B_chgrp(0,$home_directory);
500 }
501 }
502 else {
503 &B_log('ERROR',"B_userdel couldn't open the passwd file to remove a user.");
504 return 0;
505 }
506
507 #
508 # Next find out what groups the user is in, so we can call
509 # B_remove_user_from_group($user,$group)
510 #
511 # TODO: add this to the helper functions for the test suite.
512 #
513
514 my @groups = ();
515
516 # Parse /etc/group, looking for our user.
517 if (open GROUP,&getGlobal('FILE','group')) {
518 my @lines = <GROUP>;
519 close GROUP;
520
521 foreach my $line (@lines) {
522
523 # Parse the line -- first field is group, last is users in group.
524 if ($line =~ /([^\#^:]+):[^:]+:[^:]+:(.*)/) {
525 my $group = $1;
526 my $users_section = $2;
527
528 # Get the user list and check if our user is in it.
529 my @users = split /\s*,\s*/,$users_section;
530 foreach my $user (@users) {
531 if ($user_to_remove eq $user) {
532 push @groups,$group;
533 last;
534 }
535 }
536 }
537 }
538 }
539
540 # Now remove the user from each of those groups.
541 foreach my $group (@groups) {
542 &B_remove_user_from_group($user_to_remove,$group);
543 }
544
545 # Remove the user's /etc/passwd and /etc/shadow lines
546 &B_delete_line(&getGlobal('FILE','passwd'),"^$user_to_remove\\s*:");
547 &B_delete_line(&getGlobal('FILE','shadow'),"^$user_to_remove\\s*:");
548
549
550 #
551 # We should delete the user's group as well, if it's a single-user group.
552 #
553 if (open ETCGROUP,&getGlobal('FILE','group')) {
554 my @group_lines = <ETCGROUP>;
555 close ETCGROUP;
556 chomp @group_lines;
557
558 if (grep /^$user_to_remove\s*:[^:]*:[^:]*:\s*$/,@group_lines > 0) {
559 &B_groupdel($user_to_remove);
560 }
561 }
562
563}
564
565###########################################################################
566# B_groupdel($group) removes $group from /etc/group.
567###########################################################################
568
569sub B_groupdel($) {
570
571 my $group = $_[0];
572
573 # First read /etc/group to make sure the group is in there.
574 if (open GROUP,&getGlobal('FILE','group')) {
575 my @lines=<GROUP>;
576 close GROUP;
577
578 # Delete the line in /etc/group if present
579 if (grep /^$group:/,@lines > 0) {
580 # The group is named in /etc/group
581 &B_delete_line(&getGlobal('FILE','group'),"^$group:/");
582 }
583 }
584
585}
586
587
588###########################################################################
589# B_remove_user_from_group($user,$group) removes $user from $group,
590# by modifying $group's /etc/group line, pulling the user out. This
591# uses B_chunk_replace thrice to replace these patterns:
592#
593# ":\s*$user\s*," --> ":"
594# ",\s*$user" -> ""
595#
596###########################################################################
597
598sub B_remove_user_from_group($$) {
599
600 my ($user_to_remove,$group) = @_;
601
602 #
603 # We need to find the line from /etc/group that defines the group, parse
604 # it, and put it back together without this user.
605 #
606
607 # Open the group file
608 unless (open GROUP,&getGlobal('FILE','group')) {
609 &B_log('ERROR',"&B_remove_user_from_group couldn't read /etc/group to remove $user_to_remove from $group.\n");
610 return 0;
611 }
612 my @lines = <GROUP>;
613 close GROUP;
614 chomp @lines;
615
616 #
617 # Read through the lines to find the one we care about. We'll construct a
618 # replacement and then use B_replace_line to make the switch.
619 #
620
621 foreach my $line (@lines) {
622
623 if ($line =~ /^\s*$group\s*:/) {
624
625 # Parse this line.
626 my @group_entries = split ':',$line;
627 my @users = split ',',($group_entries[3]);
628
629 # Now, recreate it.
630 my $first_user = 1;
631 my $group_line = $group_entries[0] . ':' . $group_entries[1] . ':' . $group_entries[2] . ':';
632
633 # Add every user except the one we're removing.
634 foreach my $user (@users) {
635
636 # Remove whitespace.
637 $user =~ s/\s+//g;
638
639 if ($user ne $user_to_remove) {
640 # Add the user to the end of the line, prefacing
641 # it with a comma if it's not the first user.
642
643 if ($first_user) {
644 $group_line .= "$user";
645 $first_user = 0;
646 }
647 else {
648 $group_line .= ",$user";
649 }
650 }
651 }
652
653 # The line is now finished. Replace the original line.
654 $group_line .= "\n";
655 &B_replace_line(&getGlobal('FILE','group'),"^\\s*$group\\s*:",$group_line);
656 }
657
658 }
659 return 1;
660}
661
662###########################################################################
663# &B_check_owner_group($$$)
664#
665# Checks if the given file has the given owner and/or group.
666# If the given owner is "", checks group only.
667# If the given group is "", checks owner only.
668#
669# return values:
670# 1: file has the given owner and/or group
671# or file exists, and both the given owner and group are ""
672# 0: file does not has the given owner or group
673# or file does not exists
674############################################################################
675
676sub B_check_owner_group ($$$){
677 my ($fileName, $owner, $group) = @_;
678
679 if (-e $fileName) {
680 my @junk=stat ($fileName);
681 my $uid=$junk[4];
682 my $gid=$junk[5];
683
684 # Check file owner
685 if ($owner ne "") {
686 if (getpwnam($owner) != $uid) {
687 return 0;
688 }
689 }
690
691 # Check file group
692 if ($group ne "") {
693 if (getgrnam($group) != $gid) {
694 return 0;
695 }
696 }
697
698 return 1;
699 }
700 else {
701 # Something is wrong if the file not exist
702 return 0;
703 }
704}
705
706##########################################################################
707# this subroutine will test whether the given file is unowned
708##########################################################################
709sub B_is_unowned_file($) {
710 my $file =$_;
711 my $uid = (stat($file))[4];
712 my $uname = (getpwuid($uid))[0];
713 if ( $uname =~ /.+/ ) {
714 return 1;
715 }
716 return 0;
717}
718
719##########################################################################
720# this subroutine will test whether the given file is ungrouped
721##########################################################################
722sub B_is_ungrouped_file($){
723 my $file =$_;
724 my $gid = (stat($file))[5];
725 my $gname = (getgrgid($gid))[0];
726 if ( $gname =~ /.+/ ) {
727 return 1;
728 }
729 return 0;
730}
731
732
733
734
735###########################################################################
736# &B_check_permissions($$)
737#
738# Checks if the given file has the given permissions or stronger, where we
739# define stronger as "less accessible." The file argument must be fully
740# qualified, i.e. contain the absolute path.
741#
742# return values:
743# 1: file has the given permissions or better
744# 0: file does not have the given permsssions
745# undef: file permissions cannot be determined
746###########################################################################
747
748sub B_check_permissions ($$){
749 my ($fileName, $reqdPerms) = @_;
750 my $filePerms; # actual permissions
751
752
753 if (-e $fileName) {
754 if (stat($fileName)) {
755 $filePerms = (stat($fileName))[2] & 07777;
756 }
757 else {
758 &B_log ("ERROR", "Can't stat $fileName.\n");
759 return undef;
760 }
761 }
762 else {
763 # If the file does not exist, permissions are as good as they can get.
764 return 1;
765 }
766
767 #
768 # We can check whether the $filePerms are as strong by
769 # bitwise ANDing them with $reqdPerms and checking if the
770 # result is still equal to $filePerms. If it is, the
771 # $filePerms are strong enough.
772 #
773 if ( ($filePerms & $reqdPerms) == $filePerms ) {
774 return 1;
775 }
776 else {
777 return 0;
778 }
779
780}
781
782##########################################################################
783# B_permission_test($user, $previlege,$file)
784# $user can be
785# "owner"
786# "group"
787# "other"
788# $previlege can be:
789# "r"
790# "w"
791# "x"
792# "suid"
793# "sgid"
794# "sticky"
795# if previlege is set to suid or sgid or sticky, then $user can be empty
796# this sub routine test whether the $user has the specified previlige to $file
797##########################################################################
798
799sub B_permission_test($$$){
800 my ($user, $previlege, $file) = @_;
801
802 if (-e $file ) {
803 my $mode = (stat($file))[2];
804 my $bitpos;
805 # bitmap is | suid sgid sticky | rwx | rwx | rwx
806 if ($previlege =~ /suid/ ) {
807 $bitpos = 11;
808 }
809 elsif ($previlege =~ /sgid/ ) {
810 $bitpos = 10;
811 }
812 elsif ($previlege =~ /sticky/ ) {
813 $bitpos = 9;
814 }
815 else {
816 if ( $user =~ /owner/) {
817 if ($previlege =~ /r/) {
818 $bitpos = 8;
819 }
820 elsif ($previlege =~ /w/) {
821 $bitpos =7;
822 }
823 elsif ($previlege =~ /x/) {
824 $bitpos =6;
825 }
826 else {
827 return 0;
828 }
829 }
830 elsif ( $user =~ /group/) {
831 if ($previlege =~ /r/) {
832 $bitpos =5;
833 }
834 elsif ($previlege =~ /w/) {
835 $bitpos =4;
836 }
837 elsif ($previlege =~ /x/) {
838 $bitpos =3;
839 }
840 else {
841 return 0;
842 }
843 }
844 elsif ( $user =~ /other/) {
845 if ($previlege =~ /r/) {
846 $bitpos =2;
847 }
848 elsif ($previlege =~ /w/) {
849 $bitpos =1;
850 }
851 elsif ($previlege =~ /x/) {
852 $bitpos =0;
853 }
854 else {
855 return 0;
856 }
857 }
858 else {
859 return 0;
860 }
861 }
862 $mode /= 2**$bitpos;
863 if ($mode % 2) {
864 return 1;
865 }
866 return 0;
867 }
868}
869
870##########################################################################
871# this subroutine will return a list of home directory
872##########################################################################
873sub B_find_homes(){
874 # find loginable homes
875 my $logins = &getGlobal("BIN","logins");
876 my @lines = `$logins -ox`;
877 my @homes;
878 foreach my $line (@lines) {
879 chomp $line;
880 my @data = split /:/, $line;
881 if ($data[7] =~ /PS/ && $data[5] =~ /home/) {
882 push @homes, $data[5];
883 }
884 }
885 return @homes;
886}
887
888
889###########################################################################
890# B_is_executable($)
891#
892# This routine reports on whether a file is executable by the current
893# process' effective UID.
894#
895# scalar return values:
896# 0: file is not executable
897# 1: file is executable
898#
899###########################################################################
900
901sub B_is_executable($)
902{
903 my $name = shift;
904 my $executable = 0;
905
906 if (-x $name) {
907 $executable = 1;
908 }
909 return $executable;
910}
911
912###########################################################################
913# B_is_suid($)
914#
915# This routine reports on whether a file is Set-UID and owned by root.
916#
917# scalar return values:
918# 0: file is not SUID root
919# 1: file is SUID root
920#
921###########################################################################
922
923sub B_is_suid($)
924{
925 my $name = shift;
926
927 my @FileStatus = stat($name);
928 my $IsSuid = 0;
929
930 if (-u $name) #Checks existence and suid
931 {
932 if($FileStatus[4] == 0) {
933 $IsSuid = 1;
934 }
935 }
936
937 return $IsSuid;
938}
939
940###########################################################################
941# B_is_sgid($)
942#
943# This routine reports on whether a file is SGID and group owned by
944# group root (gid 0).
945#
946# scalar return values:
947# 0: file is not SGID root
948# 1: file is SGID root
949#
950###########################################################################
951
952sub B_is_sgid($)
953{
954 my $name = shift;
955
956 my @FileStatus = stat($name);
957 my $IsSgid = 0;
958
959 if (-g $name) #checks existence and sgid
960 {
961 if($FileStatus[5] == 0) {
962 $IsSgid = 1;
963 }
964 }
965
966 return $IsSgid;
967}
968
969###########################################################################
970# B_get_user_list()
971#
972# This routine outputs a list of users on the system.
973#
974###########################################################################
975
976sub B_get_user_list()
977{
978 my @users;
979 open(PASSWD,&getGlobal('FILE','passwd'));
980 while(<PASSWD>) {
981 #Get the users
982 if (/^([^:]+):/)
983 {
984 push (@users,$1);
985 }
986 }
987 return @users;
988}
989
990###########################################################################
991# B_get_group_list()
992#
993# This routine outputs a list of groups on the system.
994#
995###########################################################################
996
997sub B_get_group_list()
998{
999 my @groups;
1000 open(GROUP,&getGlobal('FILE','group'));
1001 while(my $group_line = <GROUP>) {
1002 #Get the groups
1003 if ($group_line =~ /^([^:]+):/)
1004 {
1005 push (@groups,$1);
1006 }
1007 }
1008 return @groups;
1009}
1010
1011
1012###########################################################################
1013# &B_remove_suid ($file) removes the suid bit from $file if it
1014# is set and the file exist. If you would like to remove the suid bit
1015# from /bin/ping then you need to use:
1016#
1017# &B_remove_suid("/bin/ping");
1018#
1019# &B_remove_suid respects GLOBAL_LOGONLY.
1020# &B_remove_suid uses &B_chmod to make the permission changes
1021# &B_remove_suid allows for globbing. tyler_e
1022#
1023###########################################################################
1024
1025sub B_remove_suid($) {
1026 my $file_expr = $_[0];
1027
1028 &B_log("ACTION","Removing SUID bit from \"$file_expr\".");
1029 unless ($GLOBAL_LOGONLY) {
1030 my @files = glob($file_expr);
1031
1032 foreach my $file (@files) {
1033 # check file existence
1034 if(-e $file){
1035 # stat current file to get raw permissions
1036 my $old_perm_raw = (stat $file)[2];
1037 # test to see if suidbit is set
1038 my $suid_bit = (($old_perm_raw/2048) % 2);
1039 if($suid_bit == 1){
1040 # new permission without the suid bit
1041 my $new_perm = ((($old_perm_raw/512) % 8 ) - 4) .
1042 (($old_perm_raw/64) % 8 ) .
1043 (($old_perm_raw/8) % 8 ) .
1044 (($old_perm_raw) % 8 );
1045 if(&B_chmod(oct($new_perm), $file)){
1046 &B_log("ACTION","Removed SUID bit from \"$file\".");
1047 }
1048 else {
1049 &B_log("ERROR","Could not remove SUID bit from \"$file\".");
1050 }
1051 } # No action if SUID bit is not set
1052 }# No action if file does not exist
1053 }# Repeat for each file in the file glob
1054 } # unless Global_log
1055}
1056
1057
1058
10591;
1060
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 @@
1From 456daee3ce57d3a46bf9ccf0a85ec4880ca5b262 Mon Sep 17 00:00:00 2001
2From: Andrei Dinu <andrei.adrianx.dinu@intel.com>
3Date: Tue, 4 Jun 2013 14:56:21 +0300
4Subject: [PATCH] Curses and IOLoader changes
5
6The linux distribution couldn't be identified when
7running Bastille, and the question pruning method
8couldn't get a match on the questions relevant to
9the repo, so it eliminated all quetions.
10
11After answering the questions the checkAndSaveConfig routine
12was called which was missing. Replaced it with Run_Bastille_
13with_Config which exists.
14
15Signed-off-by: Andrei Dinu <andrei.adrianx.dinu@intel.com>
16---
17 Bastille/IOLoader.pm | 2 +-
18 Bastille_Curses.pm | 4 +++-
19 2 files changed, 4 insertions(+), 2 deletions(-)
20
21diff --git a/Bastille/IOLoader.pm b/Bastille/IOLoader.pm
22index abb94d7..995d2c2 100644
23--- a/Bastille/IOLoader.pm
24+++ b/Bastille/IOLoader.pm
25@@ -68,7 +68,7 @@ sub Load_Questions($) {
26 my $UseRequiresRules = $_[0];
27
28 my ($current_module_number,$first_question) = &parse_questions();
29- $first_question = &prune_questions($UseRequiresRules,$first_question);
30+ #$first_question = &prune_questions($UseRequiresRules,$first_question);
31 $firstQuestion = $first_question;
32 &B_log("DEBUG","Load Questions, first question: $first_question");
33 &validate_questions();
34diff --git a/Bastille_Curses.pm b/Bastille_Curses.pm
35index 2e1eef4..edbbe45 100644
36--- a/Bastille_Curses.pm
37+++ b/Bastille_Curses.pm
38@@ -84,7 +84,9 @@ sub do_Bastille {
39 }
40
41 # Output answers to the script and display
42- &checkAndSaveConfig(&getGlobal('BFILE', "config"));
43+ #&checkAndSaveConfig(&getGlobal('BFILE', "config"));
44+
45+ &Run_Bastille_with_Config;
46
47 # Run Bastille
48
49--
501.7.9.5
51
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 @@
1package Bastille::API::FileContent;
2use strict;
3
4use Bastille::API;
5
6require Exporter;
7our @ISA = qw(Exporter);
8our @EXPORT_OK = qw(
9B_blank_file
10B_insert_line_after
11B_insert_line_before
12B_insert_line
13B_append_line
14B_prepend_line
15B_replace_line
16B_replace_lines
17B_replace_pattern
18B_match_line
19B_match_line_only
20B_match_chunk
21B_return_matched_lines
22B_hash_comment_line
23B_hash_uncomment_line
24B_delete_line
25B_chunk_replace
26B_print
27B_getValueFromFile
28B_getValueFromString
29
30B_TODO
31B_TODOFlags
32);
33our @EXPORT = @EXPORT_OK;
34
35
36
37###########################################################################
38# &B_blank_file ($filename,$pattern) blanks the file $filename, unless the
39# pattern $pattern is present in the file. This lets us completely redo
40# a file, if it isn't the one we put in place on a previous run...
41#
42# B_blank_file respects $GLOBAL_LOGONLY and uses B_open_plus and B_close_plus
43# so that it makes backups and only modifies files when we're not in "-v"
44# mode...
45#
46# If the file does not exist, the function does nothing, and gives an error
47# to the Error Log
48#
49###########################################################################
50
51sub B_blank_file($$) {
52
53 my ($filename,$pattern) = @_;
54 my $retval;
55
56 # If this variable is true, we won't blank the file...
57
58 my $found_pattern=0;
59
60 if ($retval=&B_open_plus (*BLANK_NEW,*BLANK_OLD,$filename) ) {
61
62 my @lines;
63
64 while (my $line = <BLANK_OLD>) {
65
66 push @lines,$line;
67 if ($line =~ $pattern) {
68 $found_pattern=1;
69 }
70 }
71
72 # Only copy the old file if the new one didn't match.
73 if ($found_pattern) {
74 while ( my $line = shift @lines ) {
75 &B_print(*BLANK_NEW,$line);
76 }
77 }
78 else {
79 &B_log("ACTION","Blanked file $filename\n");
80 }
81 &B_close_plus(*BLANK_NEW,*BLANK_OLD,$filename);
82 }
83 else {
84 &B_log("ERROR","Couldn't blank file $filename since we couldn't open it or its replacement\n");
85 }
86
87 return $retval;
88
89}
90
91###########################################################################
92# &B_insert_line_after ($filename,$pattern,$line_to_insert,$line_to_follow)
93# modifies $filename, inserting $line_to_insert unless one or more lines
94# in the file matches $pattern. The $line_to_insert will be placed
95# immediately after $line_to_follow, if it exists. If said line does not
96# exist, the line will not be inserted and this routine will return 0.
97#
98# B_insert_line uses B_open_plus and B_close_plus, so that the file
99# modified is backed up...
100#
101# Here's examples of where you might use this:
102#
103# You'd like to insert a line in Apache's configuration file, in a
104# particular section.
105#
106###########################################################################
107
108sub B_insert_line_after($$$$) {
109
110 my ($filename,$pattern,$line_to_insert,$line_to_follow) = @_;
111
112 my @lines;
113 my $found_pattern=0;
114 my $found_line_to_follow=0;
115
116 my $retval=1;
117
118 if ( &B_open_plus (*INSERT_NEW,*INSERT_OLD,$filename) ) {
119
120 # Read through the file looking for a match both on the $pattern
121 # and the line we are supposed to be inserting after...
122
123 my $ctr=1;
124 while (my $line=<INSERT_OLD>) {
125 push (@lines,$line);
126 if ($line =~ $pattern) {
127 $found_pattern=1;
128 }
129 if ( ($found_line_to_follow < 1) and ($line =~ $line_to_follow)) {
130 $found_line_to_follow=$ctr;
131 }
132 $ctr++;
133 }
134
135 # Log an error if we never found the line we were to insert after
136 unless ($found_line_to_follow ) {
137 $retval=0;
138 &B_log("ERROR","Never found the line that we were supposed to insert after in $filename\n");
139 }
140
141 # Now print the file back out, inserting our line if we should...
142
143 $ctr=1;
144 while (my $line = shift @lines) {
145 &B_print(*INSERT_NEW,$line);
146 if ( ($ctr == $found_line_to_follow) and ($found_pattern == 0) ) {
147 &B_print(*INSERT_NEW,$line_to_insert);
148 &B_log("ACTION","Inserted the following line in $filename:\n");
149 &B_log("ACTION","$line_to_insert");
150 }
151 $ctr++;
152 }
153
154 &B_close_plus (*INSERT_NEW,*INSERT_OLD,$filename);
155
156 }
157 else {
158 $retval=0;
159 &B_log("ERROR","Couldn't insert line to $filename, since open failed.");
160 }
161
162 return $retval;
163
164}
165###########################################################################
166# &B_insert_line_before ($filename,$pattern,$line_to_insert,$line_to_preceed)
167# modifies $filename, inserting $line_to_insert unless one or more lines
168# in the file matches $pattern. The $line_to_insert will be placed
169# immediately before $line_to_preceed, if it exists. If said line does not
170# exist, the line will not be inserted and this routine will return 0.
171#
172# B_insert_line uses B_open_plus and B_close_plus, so that the file
173# modified is backed up...
174#
175# Here's examples of where you might use this:
176#
177# You'd like to insert a line in Apache's configuration file, in a
178# particular section.
179#
180###########################################################################
181
182sub B_insert_line_before($$$$) {
183
184 my ($filename,$pattern,$line_to_insert,$line_to_preceed) = @_;
185
186 my @lines;
187 my $found_pattern=0;
188 my $found_line_to_preceed=0;
189
190 my $retval=1;
191
192 if ( &B_open_plus (*INSERT_NEW,*INSERT_OLD,$filename) ) {
193
194 # Read through the file looking for a match both on the $pattern
195 # and the line we are supposed to be inserting after...
196
197 my $ctr=1;
198 while (my $line=<INSERT_OLD>) {
199 push (@lines,$line);
200 if ($line =~ $pattern) {
201 $found_pattern=1;
202 }
203 if ( ($found_line_to_preceed < 1) and ($line =~ $line_to_preceed)) {
204 $found_line_to_preceed=$ctr;
205 }
206 $ctr++;
207 }
208
209 # Log an error if we never found the line we were to preceed
210 unless ($found_line_to_preceed ) {
211 $retval=0;
212 &B_log("ERROR","Never found the line that we were supposed to insert before in $filename\n");
213 }
214
215 # Now print the file back out, inserting our line if we should...
216
217 $ctr=1;
218 while (my $line = shift @lines) {
219 if ( ($ctr == $found_line_to_preceed) and ($found_pattern == 0) ) {
220 &B_print(*INSERT_NEW,$line_to_insert);
221 &B_log("ACTION","Inserted the following line in $filename:\n");
222 &B_log("ACTION","$line_to_insert");
223 }
224 &B_print(*INSERT_NEW,$line);
225 $ctr++;
226 }
227
228 &B_close_plus (*INSERT_NEW,*INSERT_OLD,$filename);
229
230 }
231 else {
232 $retval=0;
233 &B_log("ERROR","Couldn't insert line to $filename, since open failed.");
234 }
235
236 return $retval;
237
238}
239
240###########################################################################
241# &B_insert_line ($filename,$pattern,$line_to_insert,$line_to_follow)
242#
243# has been renamed to B_insert_line_after()
244#
245# This name will continue to work, as a shim for code that has not been
246# transitioned.
247###########################################################################
248
249sub B_insert_line($$$$) {
250
251 my $rtn_value = &B_insert_line_after(@_);
252
253 return ($rtn_value);
254}
255
256
257###########################################################################
258# &B_append_line ($filename,$pattern,$line_to_append) modifies $filename,
259# appending $line_to_append unless one or more lines in the file matches
260# $pattern. This is an enhancement to the append_line_if_no_such_line_exists
261# idea.
262#
263# Additionally, if $pattern is set equal to "", the line is always appended.
264#
265# B_append_line uses B_open_plus and B_close_plus, so that the file
266# modified is backed up...
267#
268# Here's examples of where you might use this:
269#
270# You'd like to add a root line to /etc/ftpusers if none exists.
271# You'd like to add a Options Indexes line to Apache's config. file,
272# after you delete all Options lines from said config file.
273#
274###########################################################################
275
276sub B_append_line($$$) {
277
278 my ($filename,$pattern,$line_to_append) = @_;
279
280 my $found_pattern=0;
281 my $retval=1;
282
283 if ( &B_open_plus (*APPEND_NEW,*APPEND_OLD,$filename) ) {
284 while (my $line=<APPEND_OLD>) {
285 &B_print(*APPEND_NEW,$line);
286 if ($line =~ $pattern) {
287 $found_pattern=1;
288 }
289 }
290 # Changed != 0 to $pattern so that "" works instead of 0 and perl
291 # does not give the annoying
292 # Argument "XX" isn't numeric in ne at ...
293 if ( $pattern eq "" or ! $found_pattern ) {
294 &B_print(*APPEND_NEW,$line_to_append);
295 &B_log("ACTION","Appended the following line to $filename:\n");
296 &B_log("ACTION","$line_to_append");
297 }
298 &B_close_plus (*APPEND_NEW,*APPEND_OLD,$filename);
299 }
300 else {
301 $retval=0;
302 &B_log("ERROR","# Couldn't append line to $filename, since open failed.");
303 }
304
305 return $retval;
306
307}
308
309###########################################################################
310# &B_prepend_line ($filename,$pattern,$line_to_prepend) modifies $filename,
311# pre-pending $line_to_prepend unless one or more lines in the file matches
312# $pattern. This is an enhancement to the prepend_line_if_no_such_line_exists
313# idea.
314#
315# B_prepend_line uses B_open_plus and B_close_plus, so that the file
316# modified is backed up...
317#
318# Here's examples of where you might use this:
319#
320# You'd like to insert the line "auth required pam_deny.so" to the top
321# of the PAM stack file /etc/pam.d/rsh to totally deactivate rsh.
322#
323###########################################################################
324
325sub B_prepend_line($$$) {
326
327 my ($filename,$pattern,$line_to_prepend) = @_;
328
329 my @lines;
330 my $found_pattern=0;
331 my $retval=1;
332
333 if ( &B_open_plus (*PREPEND_NEW,*PREPEND_OLD,$filename) ) {
334 while (my $line=<PREPEND_OLD>) {
335 push (@lines,$line);
336 if ($line =~ $pattern) {
337 $found_pattern=1;
338 }
339 }
340 unless ($found_pattern) {
341 &B_print(*PREPEND_NEW,$line_to_prepend);
342 }
343 while (my $line = shift @lines) {
344 &B_print(*PREPEND_NEW,$line);
345 }
346
347 &B_close_plus (*PREPEND_NEW,*PREPEND_OLD,$filename);
348
349 # Log the action
350 &B_log("ACTION","Pre-pended the following line to $filename:\n");
351 &B_log("ACTION","$line_to_prepend");
352 }
353 else {
354 $retval=0;
355 &B_log("ERROR","Couldn't prepend line to $filename, since open failed.\n");
356 }
357
358 return $retval;
359
360}
361
362
363###########################################################################
364# &B_replace_line ($filename,$pattern,$line_to_switch_in) modifies $filename,
365# replacing any lines matching $pattern with $line_to_switch_in.
366#
367# It returns the number of lines it replaced (or would have replaced, if
368# LOGONLY mode wasn't on...)
369#
370# B_replace_line uses B_open_plus and B_close_plus, so that the file
371# modified is backed up...
372#
373# Here an example of where you might use this:
374#
375# You'd like to replace any Options lines in Apache's config file with:
376# Options Indexes FollowSymLinks
377#
378###########################################################################
379
380sub B_replace_line($$$) {
381
382 my ($filename,$pattern,$line_to_switch_in) = @_;
383 my $retval=0;
384
385 if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) {
386 while (my $line=<REPLACE_OLD>) {
387 unless ($line =~ $pattern) {
388 &B_print(*REPLACE_NEW,$line);
389 }
390 else {
391 # Don't replace the line if it's already there.
392 unless ($line eq $line_to_switch_in) {
393 &B_print(*REPLACE_NEW,$line_to_switch_in);
394
395 $retval++;
396 &B_log("ACTION","File modification in $filename -- replaced line\n" .
397 "$line\n" .
398 "with:\n" .
399 "$line_to_switch_in");
400 }
401 # But if it is there, make sure it stays there! (by Paul Allen)
402 else {
403 &B_print(*REPLACE_NEW,$line);
404 }
405 }
406 }
407 &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename);
408 }
409 else {
410 $retval=0;
411 &B_log("ERROR","Couldn't replace line(s) in $filename because open failed.\n");
412 }
413
414 return $retval;
415}
416
417###########################################################################
418# &B_replace_lines ($filename,$patterns_and_substitutes) modifies $filename,
419# replacing the line matching the nth $pattern specified in $patterns_and_substitutes->[n]->[0]
420# with the corresponding substitutes in $patterns_and_substitutes->[n]->-[1]
421#
422# It returns the number of lines it replaced (or would have replaced, if
423# LOGONLY mode wasn't on...)
424#
425# B_replace_lines uses B_open_plus and B_close_plus, so that the file
426# modified is backed up...
427#
428# Here an example of where you might use this:
429#
430# You'd like to replace /etc/opt/ssh/sshd_config file
431# (^#|^)Protocol\s+(.*)\s*$ ==> Protocol 2
432# (^#|^)X11Forwarding\s+(.*)\s*$ ==> X11Forwarding yes
433# (^#|^)IgnoreRhosts\s+(.*)\s*$ ==> gnoreRhosts yes
434# (^#|^)RhostsAuthentication\s+(.*)\s*$ ==> RhostsAuthentication no
435# (^#|^)RhostsRSAAuthentication\s+(.*)\s*$ ==> RhostsRSAAuthentication no
436# (^#|^)PermitRootLogin\s+(.*)\s*$ ==> PermitRootLogin no
437# (^#|^)PermitEmptyPasswords\s+(.*)\s*$ ==> PermitEmptyPasswords no
438# my $patterns_and_substitutes = [
439# [ '(^#|^)Protocol\s+(.*)\s*$' => 'Protocol 2'],
440# ['(^#|^)X11Forwarding\s+(.*)\s*$' => 'X11Forwarding yes'],
441# ['(^#|^)IgnoreRhosts\s+(.*)\s*$' => 'gnoreRhosts yes'],
442# ['(^#|^)RhostsAuthentication\s+(.*)\s*$' => 'RhostsAuthentication no'],
443# ['(^#|^)RhostsRSAAuthentication\s+(.*)\s*$' => 'RhostsRSAAuthentication no'],
444# ['(^#|^)PermitRootLogin\s+(.*)\s*$' => 'PermitRootLogin no'],
445# ['(^#|^)PermitEmptyPasswords\s+(.*)\s*$' => 'PermitEmptyPasswords no']
446#]
447# B_replaces_lines($sshd_config,$patterns_and_substitutes);
448###########################################################################
449
450sub B_replace_lines($$){
451 my ($filename, $pairs) = @_;
452 my $retval = 0;
453 if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) {
454 while (my $line = <REPLACE_OLD>) {
455 my $switch;
456 my $switch_before = $line;
457 chomp($line);
458 foreach my $pair (@$pairs) {
459 $switch = 0;
460
461 my $pattern = $pair->[0] ;
462 my $replace = $pair->[1];
463 my $evalstr = '$line' . "=~ s/$pattern/$replace/";
464 eval $evalstr;
465 if ($@) {
466 &B_log("ERROR", "eval $evalstr failed.\n");
467 }
468 #if ( $line =~ s/$pair->[0]/$pair->[1]/) {
469 # $switch = 1;
470 # last;
471 #}
472 }
473 &B_print(*REPLACE_NEW,"$line\n");
474 if ($switch) {
475 $retval++;
476 B_log("ACTION","File modification in $filename -- replaced line\n" .
477 "$switch_before\n" .
478 "with:\n" .
479 "$line\n");
480 }
481 }
482 &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename);
483 return 1;
484 }
485 else {
486 $retval=0;
487 &B_log("ERROR","Couldn't replace line(s) in $filename because open failed.\n");
488 }
489}
490
491################################################################################################
492# &B_replace_pattern ($filename,$pattern,$pattern_to_remove,$text_to_switch_in)
493# modifies $filename, acting on only lines that match $pattern, replacing a
494# string that matches $pattern_to_remove with $text_to_switch_in.
495#
496# Ex:
497# B_replace_pattern('/etc/httpd.conf','^\s*Options.*\bIncludes\b','Includes','IncludesNoExec')
498#
499# replaces all "Includes" with "IncludesNoExec" on Apache Options lines.
500#
501# It returns the number of lines it altered (or would have replaced, if
502# LOGONLY mode wasn't on...)
503#
504# B_replace_pattern uses B_open_plus and B_close_plus, so that the file
505# modified is backed up...
506#
507#################################################################################################
508
509sub B_replace_pattern($$$$) {
510
511 my ($filename,$pattern,$pattern_to_remove,$text_to_switch_in) = @_;
512 my $retval=0;
513
514 if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) {
515 while (my $line=<REPLACE_OLD>) {
516 unless ($line =~ $pattern) {
517 &B_print(*REPLACE_NEW,$line);
518 }
519 else {
520 my $orig_line =$line;
521 $line =~ s/$pattern_to_remove/$text_to_switch_in/;
522
523 &B_print(*REPLACE_NEW,$line);
524
525 $retval++;
526 &B_log("ACTION","File modification in $filename -- replaced line\n" .
527 "$orig_line\n" .
528 "via pattern with:\n" .
529 "$line\n\n");
530 }
531 }
532 &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename);
533 }
534 else {
535 $retval=0;
536 &B_log("ERROR","Couldn't pattern-replace line(s) in $filename because open failed.\n");
537 }
538
539 return $retval;
540}
541
542
543###########################################################################
544# &B_match_line($file,$pattern);
545#
546# This subroutine will return a 1 if the pattern specified can be matched
547# against the file specified. It will return a 0 otherwise.
548#
549# return values:
550# 0: pattern not in file or the file is not readable
551# 1: pattern is in file
552###########################################################################
553sub B_match_line($$) {
554 # file to be checked and pattern to check for.
555 my ($file,$pattern) = @_;
556 # if the file is readable then
557 if(-r $file) {
558 # if the file can be opened then
559 if(open FILE,"<$file") {
560 # look at each line in the file
561 while (my $line = <FILE>) {
562 # if a line matches the pattern provided then
563 if($line =~ $pattern) {
564 # return the pattern was found
565 B_log('DEBUG','Pattern: ' . $pattern . ' matched in file: ' .
566 $file . "\n");
567 return 1;
568 }
569 }
570 }
571 # if the file cann't be opened then
572 else {
573 # send a note to that affect to the errorlog
574 &B_log("ERROR","Unable to open file for read.\n$file\n$!\n");
575 }
576 }
577 B_log('DEBUG','Pattern: ' . $pattern . ' not matched in file: ' .
578 $file . "\n");
579 # the provided pattern was not matched against a line in the file
580 return 0;
581}
582
583###########################################################################
584# &B_match_line_only($file,$pattern);
585#
586# This subroutine checks if the specified pattern can be matched and if
587# it's the only content in the file. The only content means it's only but
588# may have several copies in the file.
589#
590# return values:
591# 0: pattern not in file or pattern is not the only content
592# or the file is not readable
593# 1: pattern is in file and it's the only content
594############################################################################
595sub B_match_line_only($$) {
596 my ($file,$pattern) = @_;
597
598 # if matched, set to 1 later
599 my $retval = 0;
600
601 # if the file is readable then
602 if(-r $file) {
603 # if the file can be opened then
604 if(&B_open(*FILED, $file)) {
605 # pattern should be matched at least once
606 # pattern can not be mismatched
607 while (my $line = <FILED>) {
608 if ($line =~ $pattern) {
609 $retval = 1;
610 }
611 else {
612 &B_close(*FILED);
613 return 0;
614 }
615 }
616 }
617 &B_close(*FILED);
618 }
619
620 return $retval;
621}
622
623###########################################################################
624# &B_return_matched_lines($file,$pattern);
625#
626# This subroutine returns lines in a file matching a given regular
627# expression, when called in the default list mode. When called in scalar
628# mode, returns the number of elements found.
629###########################################################################
630sub B_return_matched_lines($$)
631{
632 my ($filename,$pattern) = @_;
633 my @lines = ();
634
635 open(READFILE, $filename);
636 while (<READFILE>) {
637 chomp;
638 next unless /$pattern/;
639 push(@lines, $_);
640 }
641 if (wantarray)
642 {
643 return @lines;
644 }
645 else
646 {
647 return scalar (@lines);
648 }
649}
650
651###########################################################################
652# &B_match_chunk($file,$pattern);
653#
654# This subroutine will return a 1 if the pattern specified can be matched
655# against the file specified on a line-agnostic form. This allows for
656# patterns which by necessity must match against a multi-line pattern.
657# This is the natural analogue to B_replace_chunk, which was created to
658# provide multi-line capability not provided by B_replace_line.
659#
660# return values:
661# 0: pattern not in file or the file is not readable
662# 1: pattern is in file
663###########################################################################
664
665sub B_match_chunk($$) {
666
667 my ($file,$pattern) = @_;
668 my @lines;
669 my $big_long_line;
670 my $retval=1;
671
672 open CHUNK_FILE,$file;
673
674 # Read all lines into one scalar.
675 @lines = <CHUNK_FILE>;
676 close CHUNK_FILE;
677
678 foreach my $line ( @lines ) {
679 $big_long_line .= $line;
680 }
681
682 # Substitution routines get weird unless last line is terminated with \n
683 chomp $big_long_line;
684 $big_long_line .= "\n";
685
686 # Exit if we don't find a match
687 unless ($big_long_line =~ $pattern) {
688 $retval = 0;
689 }
690
691 return $retval;
692}
693
694###########################################################################
695# &B_hash_comment_line ($filename,$pattern) modifies $filename, replacing
696# any lines matching $pattern with a "hash-commented" version, like this:
697#
698#
699# finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
700# becomes:
701# #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
702#
703# Also:
704# tftp dgram udp wait root /usr/lbin/tftpd tftpd\
705# /opt/ignite\
706# /var/opt/ignite
707# becomes:
708# #tftp dgram udp wait root /usr/lbin/tftpd tftpd\
709# # /opt/ignite\
710# # /var/opt/ignite
711#
712#
713# B_hash_comment_line uses B_open_plus and B_close_plus, so that the file
714# modified is backed up...
715#
716###########################################################################
717
718sub B_hash_comment_line($$) {
719
720 my ($filename,$pattern) = @_;
721 my $retval=1;
722
723 if ( &B_open_plus (*HASH_NEW,*HASH_OLD,$filename) ) {
724 my $line;
725 while ($line=<HASH_OLD>) {
726 unless ( ($line =~ $pattern) and ($line !~ /^\s*\#/) ) {
727 &B_print(*HASH_NEW,$line);
728 }
729 else {
730 &B_print(*HASH_NEW,"#$line");
731 &B_log("ACTION","File modification in $filename -- hash commented line\n" .
732 "$line\n" .
733 "like this:\n" .
734 "#$line\n\n");
735 # while the line has a trailing \ then we should also comment out the line below
736 while($line =~ m/\\\n$/) {
737 if($line=<HASH_OLD>) {
738 &B_print(*HASH_NEW,"#$line");
739 &B_log("ACTION","File modification in $filename -- hash commented line\n" .
740 "$line\n" .
741 "like this:\n" .
742 "#$line\n\n");
743 }
744 else {
745 $line = "";
746 }
747 }
748
749 }
750 }
751 &B_close_plus (*HASH_NEW,*HASH_OLD,$filename);
752 }
753 else {
754 $retval=0;
755 &B_log("ERROR","Couldn't hash-comment line(s) in $filename because open failed.\n");
756 }
757
758 return $retval;
759}
760
761
762###########################################################################
763# &B_hash_uncomment_line ($filename,$pattern) modifies $filename,
764# removing any commenting from lines that match $pattern.
765#
766# #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
767# becomes:
768# finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd
769#
770#
771# B_hash_uncomment_line uses B_open_plus and B_close_plus, so that the file
772# modified is backed up...
773#
774###########################################################################
775
776sub B_hash_uncomment_line($$) {
777
778 my ($filename,$pattern) = @_;
779 my $retval=1;
780
781 if ( &B_open_plus (*HASH_NEW,*HASH_OLD,$filename) ) {
782 my $line;
783 while ($line=<HASH_OLD>) {
784 unless ( ($line =~ $pattern) and ($line =~ /^\s*\#/) ) {
785 &B_print(*HASH_NEW,$line);
786 }
787 else {
788 $line =~ /^\s*\#+(.*)$/;
789 $line = "$1\n";
790
791 &B_print(*HASH_NEW,"$line");
792 &B_log("ACTION","File modification in $filename -- hash uncommented line\n");
793 &B_log("ACTION",$line);
794 # while the line has a trailing \ then we should also uncomment out the line below
795 while($line =~ m/\\\n$/) {
796 if($line=<HASH_OLD>) {
797 $line =~ /^\s*\#+(.*)$/;
798 $line = "$1\n";
799 &B_print(*HASH_NEW,"$line");
800 &B_log("ACTION","File modification in $filename -- hash uncommented line\n");
801 &B_log("ACTION","#$line");
802 &B_log("ACTION","like this:\n");
803 &B_log("ACTION","$line");
804 }
805 else {
806 $line = "";
807 }
808 }
809 }
810 }
811 &B_close_plus (*HASH_NEW,*HASH_OLD,$filename);
812 }
813 else {
814 $retval=0;
815 &B_log("ERROR","Couldn't hash-uncomment line(s) in $filename because open failed.\n");
816 }
817
818 return $retval;
819}
820
821
822
823###########################################################################
824# &B_delete_line ($filename,$pattern) modifies $filename, deleting any
825# lines matching $pattern. It uses B_replace_line to do this.
826#
827# B_replace_line uses B_open_plus and B_close_plus, so that the file
828# modified is backed up...
829#
830# Here an example of where you might use this:
831#
832# You'd like to remove any timeout= lines in /etc/lilo.conf, so that your
833# delay=1 modification will work.
834
835#
836###########################################################################
837
838
839sub B_delete_line($$) {
840
841 my ($filename,$pattern)=@_;
842 my $retval=&B_replace_line($filename,$pattern,"");
843
844 return $retval;
845}
846
847
848###########################################################################
849# &B_chunk_replace ($file,$pattern,$replacement) reads $file replacing the
850# first occurrence of $pattern with $replacement.
851#
852###########################################################################
853
854sub B_chunk_replace($$$) {
855
856 my ($file,$pattern,$replacement) = @_;
857
858 my @lines;
859 my $big_long_line;
860 my $retval=1;
861
862 &B_open (*OLDFILE,$file);
863
864 # Read all lines into one scalar.
865 @lines = <OLDFILE>;
866 &B_close (*OLDFILE);
867 foreach my $line ( @lines ) {
868 $big_long_line .= $line;
869 }
870
871 # Substitution routines get weird unless last line is terminated with \n
872 chomp $big_long_line;
873 $big_long_line .= "\n";
874
875 # Exit if we don't find a match
876 unless ($big_long_line =~ $pattern) {
877 return 0;
878 }
879
880 $big_long_line =~ s/$pattern/$replacement/s;
881
882 $retval=&B_open_plus (*NEWFILE,*OLDFILE,$file);
883 if ($retval) {
884 &B_print (*NEWFILE,$big_long_line);
885 &B_close_plus (*NEWFILE,*OLDFILE,$file);
886 }
887
888 return $retval;
889}
890
891###########################################################################
892# &B_print ($handle,@list) prints the items of @list to the file handle
893# $handle. It logs the action and respects the $GLOBAL_LOGONLY variable.
894#
895###########################################################################
896
897sub B_print {
898 my $handle=shift @_;
899
900 my $result=1;
901
902 unless ($GLOBAL_LOGONLY) {
903 $result=print $handle @_;
904 }
905
906 ($handle) = "$handle" =~ /[^:]+::[^:]+::([^:]+)/;
907
908 $result;
909}
910
911
912##########################################################################
913# &B_getValueFromFile($regex,$file);
914# Takes a regex with a single group "()" and returns the unique value
915# on any non-commented lines
916# This (and B_return_matched_lines are only used in this file, though are
917# probably more generally useful. For now, leaving these here serve the following
918#functions:
919# a) still gets exported/associated as part of the Test_API package, and
920# is still availble for a couple operations that can't be deferred to the
921# main test loop, as they save values so that individual tests don't have to
922# recreate (copy / paste) the logic to get them.
923#
924# It also avoids the circular "use" if we incldued "use Test API" at the top
925# of this file (Test API "uses" this file.
926# Returns the uncommented, unique values of a param=value pair.
927#
928# Return values:
929# 'Not Defined' if the value is not present or not uniquely defined.
930# $value if the value is present and unique
931#
932###########################################################################
933sub B_getValueFromFile ($$){
934 my $inputRegex=$_[0];
935 my $file=$_[1];
936 my ($lastvalue,$value)='';
937
938 my @lines=&B_return_matched_lines($file, $inputRegex);
939
940 return &B_getValueFromString($inputRegex,join('/n',@lines));
941}
942
943##########################################################################
944# &B_getValueFromString($param,$string);
945# Takes a regex with a single group "()" and returns the unique value
946# on any non-commented lines
947# This (and B_return_matched_lines are only used in this file, though are
948# probably more generally useful. For now, leaving these here serve the following
949#functions:
950# a) still gets exported/associated as part of the Test_API package, and
951# is still availble for a couple operations that can't be deferred to the
952# main test loop, as they save values so that individual tests don't have to
953# recreate (copy / paste) the logic to get them.
954#
955# It also avoids the circular "use" if we incldued "use Test API" at the top
956# of this file (Test API "uses" this file.
957# Returns the uncommented, unique values of a param=value pair.
958#
959# Return values:
960# 'Not Unique' if the value is not uniquely defined.
961# undef if the value isn't defined at all
962# $value if the value is present and unique
963#
964###########################################################################
965sub B_getValueFromString ($$){
966 my $inputRegex=$_[0];
967 my $inputString=$_[1];
968 my $lastValue='';
969 my $value='';
970
971 my @lines=split(/\n/,$inputString);
972
973 &B_log("DEBUG","B_getvaluefromstring called with regex: $inputRegex and input: " .
974 $inputString);
975 foreach my $line (grep(/$inputRegex/,@lines)) {
976 $line =~ /$inputRegex/;
977 $value=$1;
978 if (($lastValue eq '') and ($value ne '')) {
979 $lastValue = $value;
980 } elsif (($lastValue ne $value) and ($value ne '')) {
981 B_log("DEBUG","getvaluefromstring returned Not Unique");
982 return 'Not Unique';
983 }
984 }
985 if ((not(defined($value))) or ($value eq '')) {
986 &B_log("DEBUG","Could not find regex match in string");
987 return undef;
988 } else {
989 &B_log("DEBUG","B_getValueFromString Found: $value ; using: $inputRegex");
990 return $value;
991 }
992}
993
994###############################################################
995# This function adds something to the To Do List.
996# Arguments:
997# 1) The string you want to add to the To Do List.
998# 2) Optional: Question whose TODOFlag should be set to indicate
999# A pending manual action in subsequent reports. Only skip this
1000# If there's no security-audit relevant action you need the user to
1001# accomplish
1002# Ex:
1003# &B_TODO("------\nInstalling IPFilter\n----\nGo get Ipfilter","IPFilter.install_ipfilter");
1004#
1005#
1006# Returns:
1007# 0 - If error condition
1008# True, if sucess, specifically:
1009# "appended" if the append operation was successful
1010# "exists" if no change was made since the entry was already present
1011###############################################################
1012sub B_TODO ($;$) {
1013 my $text = $_[0];
1014 my $FlaggedQuestion = $_[1];
1015 my $multilineString = "";
1016
1017 # trim off any leading and trailing new lines, regexes separated for "clarity"
1018 $text =~ s/^\n+(.*)/$1/;
1019 $text =~ s/(.*)\n+$/$1/;
1020
1021 if ( ! -e &getGlobal('BFILE',"TODO") ) {
1022 # Make the TODO list file for HP-UX Distro
1023 &B_create_file(&getGlobal('BFILE', "TODO"));
1024 &B_append_line(&getGlobal('BFILE', "TODO"),'a$b',
1025 "Please take the steps below to make your system more secure,\n".
1026 "then delete the item from this file and record what you did along\n".
1027 "with the date and time in your system administration log. You\n".
1028 "will need that information in case you ever need to revert your\n".
1029 "changes.\n\n");
1030 }
1031
1032
1033 if (open(TODO,"<" . &getGlobal('BFILE', "TODO"))) {
1034 while (my $line = <TODO>) {
1035 # getting rid of all meta characters.
1036 $line =~ s/(\\|\||\(|\)|\[|\]|\{|\}|\^|\$|\*|\+|\?|\.)//g;
1037 $multilineString .= $line;
1038 }
1039 chomp $multilineString;
1040 $multilineString .= "\n";
1041
1042 close(TODO);
1043 }
1044 else {
1045 &B_log("ERROR","Unable to read TODO.txt file.\n" .
1046 "The following text could not be appended to the TODO list:\n" .
1047 $text .
1048 "End of TODO text\n");
1049 return 0; #False
1050 }
1051
1052 my $textPattern = $text;
1053
1054 # getting rid of all meta characters.
1055 $textPattern =~ s/(\\|\||\(|\)|\[|\]|\{|\}|\^|\$|\*|\+|\?|\.)//g;
1056
1057 if( $multilineString !~ "$textPattern") {
1058 my $datestamp = "{" . localtime() . "}";
1059 unless ( &B_append_line(&getGlobal('BFILE', "TODO"), "", $datestamp . "\n" . $text . "\n\n\n") ) {
1060 &B_log("ERROR","TODO Failed for text: " . $text );
1061 }
1062 #Note that we only set the flag on the *initial* entry in the TODO File
1063 #Not on subsequent detection. This is to avoid the case where Bastille
1064 #complains on a subsequent Bastille run of an already-performed manual
1065 #action that the user neglected to delete from the TODO file.
1066 # It does, however lead to a report of "nonsecure" when the user
1067 #asked for the TODO item, performed it, Bastille detected that and cleared the
1068 # Item, and then the user unperformed the action. I think this is proper behavior.
1069 # rwf 06/06
1070
1071 if (defined($FlaggedQuestion)) {
1072 &B_TODOFlags("set",$FlaggedQuestion);
1073 }
1074 return "appended"; #evals to true, and also notes what happened
1075 } else {
1076 return "exists"; #evals to true, and also
1077 }
1078
1079}
1080
1081
1082#####################################################################
1083# &B_TODOFlags()
1084#
1085# This is the interface to the TODO flags. Test functions set these when they
1086# require a TODO item to be completed to get to a "secure" state.
1087# The prune/reporting function checks these to ensure no flags are set before
1088# reporting an item "secure"
1089# "Methods" are load | save | isSet <Question> | set <Question> | unset <Question>
1090#
1091######################################################################
1092
1093sub B_TODOFlags($;$) {
1094 my $action = $_[0];
1095 my $module = $_[1];
1096
1097 use File::Spec;
1098
1099 my $todo_flag = &getGlobal("BFILE","TODOFlag");
1100
1101 &B_log("DEBUG","B_TODOFlags action: $action , module: $module");
1102
1103 if ($action eq "load") {
1104 if (-e $todo_flag ) {
1105 &B_open(*TODO_FLAGS, $todo_flag);
1106 my @lines = <TODO_FLAGS>;
1107 foreach my $line (@lines) {
1108 chomp($line);
1109 $GLOBAL_CONFIG{"$line"}{"TODOFlag"}="yes";
1110 }
1111 return (&B_close(*TODO_FLAGS)); #return success of final close
1112 } else {
1113 return 1; #No-op is okay
1114 }
1115 } elsif ($action eq "save") {
1116 # Make sure the file exists, else create
1117 #Note we use open_plus and and create file, so if Bastille is
1118 #reverted, all the flags will self-clear (file deleted)
1119 my $flagNumber = 0;
1120 my $flagData = '';
1121 foreach my $key (keys %GLOBAL_CONFIG) {
1122 if ($GLOBAL_CONFIG{$key}{"TODOFlag"} eq "yes") {
1123 ++$flagNumber;
1124 $flagData .= "$key\n";
1125 }
1126 }
1127 if (not( -e $todo_flag)) {
1128 &B_log("DEBUG","Initializing TODO Flag file: $todo_flag");
1129 &B_create_file($todo_flag); # Make sure it exists
1130 }
1131 &B_blank_file($todo_flag,
1132 "This will not appear in the file; ensures blanking");
1133 return &B_append_line($todo_flag, "", "$flagData"); #return success of save
1134 } elsif (($action eq "isSet") and ($module ne "")) {
1135 if ($GLOBAL_CONFIG{"$module"}{"TODOFlag"} eq "yes") {
1136 return 1; #TRUE
1137 } else {
1138 return 0; #FALSE
1139 }
1140 } elsif (($action eq "set") and ($module ne "")) {
1141 $GLOBAL_CONFIG{"$module"}{"TODOFlag"} = "yes";
1142 } elsif (($action eq "clear") and ($module ne "")) {
1143 $GLOBAL_CONFIG{"$module"}{"TODOFlag"} = "";
1144 } else {
1145 &B_log("ERROR","TODO_Flag Called with invalid parameters: $action , $module".
1146 "audit report may be incorrect.");
1147 return 0; #FALSE
1148 }
1149}
1150
11511;
1152
1153
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 @@
1package Bastille::API::HPSpecific;
2
3use strict;
4use Bastille::API;
5use Bastille::API::FileContent;
6
7require Exporter;
8our @ISA = qw(Exporter);
9our @EXPORT_OK = qw(
10getIPFLocation
11getGlobalSwlist
12B_check_system
13B_swmodify
14B_load_ipf_rules
15B_Schedule
16B_ch_rc
17B_set_value
18B_chperm
19B_install_jail
20B_list_processes
21B_list_full_processes
22B_deactivate_inetd_service
23B_get_rc
24B_set_rc
25B_chrootHPapache
26isSystemTrusted
27isTrustedMigrationAvailable
28checkServiceOnHPUX
29B_get_path
30convertToTrusted
31isOKtoConvert
32convertToShadow
33getSupportedSettings
34B_get_sec_value
35secureIfNoNameService
36isUsingRemoteNameService
37remoteServiceCheck
38remoteNISPlusServiceCheck
39B_create_nsswitch_file
40B_combine_service_results
41
42%priorBastilleNDD
43%newNDD
44);
45our @EXPORT = @EXPORT_OK;
46
47
48
49# "Constants" for use both in testing and in lock-down
50our %priorBastilleNDD = (
51 "ip_forward_directed_broadcasts" =>["ip", "0"],
52 "ip_forward_src_routed" =>["ip", "0"],
53 "ip_forwarding" =>["ip", "0"],
54 "ip_ire_gw_probe" =>["ip", "0"],
55 "ip_pmtu_strategy" =>["ip", "1"],
56 "ip_respond_to_echo_broadcast" =>["ip", "0"],
57 "ip_send_redirects" =>["ip", "0"],
58 "ip_send_source_quench" =>["ip", "0"],
59 "tcp_syn_rcvd_max" =>["tcp","1000"],
60 "tcp_conn_request_max" =>["tcp","4096"] );
61
62our %newNDD = (
63 "ip_forward_directed_broadcasts" =>["ip", "0"],
64 "ip_forward_src_routed" =>["ip", "0"],
65 "ip_forwarding" =>["ip", "0"],
66 "ip_ire_gw_probe" =>["ip", "0"],
67 "ip_pmtu_strategy" =>["ip", "1"],
68 "ip_respond_to_echo_broadcast" =>["ip", "0"],
69 "ip_send_redirects" =>["ip", "0"],
70 "ip_send_source_quench" =>["ip", "0"],
71 "tcp_syn_rcvd_max" =>["tcp","4096"],
72 "tcp_conn_request_max" =>["tcp","4096"],
73 "arp_cleanup_interval" =>["arp","60000"],
74 "ip_respond_to_timestamp" =>["ip", "0"],
75 "ip_respond_to_timestamp_broadcast" => ["ip","0"] );
76
77
78####################################################################
79#
80# This module makes up the HP-UX specific API routines.
81#
82####################################################################
83#
84# Subroutine Listing:
85# &HP_ConfigureForDistro: adds all used file names to global
86# hashes and generates a global IPD
87# hash for SD modification lookup.
88#
89# &getGlobalSwlist($): Takes a fully qualified file name
90# and returns product:filset info
91# for that file. returns undef if
92# the file is not present in the IPD
93#
94# &B_check_system: Runs a series of system queries to
95# determine if Bastille can be safely
96# ran on the current system.
97#
98# &B_swmodify($): Takes a file name and runs the
99# swmodify command on it so that the
100# IPD is updated after changes
101#
102# &B_System($$): Takes a system command and the system
103# command that should be used to revert
104# whatever was done. Returns 1 on
105# success and 0 on failure
106#
107# &B_Backtick($) Takes a command to run and returns its stdout
108# to be used in place of the prior prevelent use
109# of un-error-handled backticks
110#
111# &B_load_ipf_rules($): Loads a set of ipfrules into ipf, storing
112# current rules for later reversion.
113#
114# &B_Schedule($$): Takes a pattern and a crontab line.
115# Adds or replaces the crontab line to
116# the crontab file, depending on if a
117# line matches the pattern
118#
119# &B_ch_rc($$): Takes a the rc.config.d flag name and
120# new value as well as the init script
121# location. This will stop a services
122# and set the service so that it will
123# not be restarted.
124#
125# &B_set_value($$$): Takes a param, value, and a filename
126# and sets the given value in the file.
127# Uses ch_rc, but could be rewritten using
128# Bastille API calls to make it work on Linux
129#
130# &B_TODO($): Appends the give string to the TODO.txt
131# file.
132#
133# &B_chperm($$$$): Takes new perm owner and group of given
134# file. TO BE DEPRECATED!!!
135#
136# &B_install_jail($$): Takes the jail name and the jail config
137# script location for a give jail...
138# These scripts can be found in the main
139# directory e.g. jail.bind.hpux
140#
141#####################################################################
142
143##############################################################################
144#
145# HP-UX Bastille directory structure
146#
147##############################################################################
148#
149# /opt/sec_mgmt/bastille/bin/ -- location of Bastille binaries
150# /opt/sec_mgmt/bastille/lib/ -- location of Bastille modules
151# /opt/sec_mgmt/bastille/doc/ -- location of Bastille doc files
152#
153# /etc/opt/sec_mgmt/bastille/ -- location of Bastille config files
154#
155# /var/opt/sec_mgmt/bastille/log -- location of Bastille log files
156# /var/opt/sec_mgmt/bastille/revert -- directory holding all Bastille-
157# created revert scripts
158# /var/opt/sec_mgmt/bastille/revert/backup -- directory holding the original
159# files that Bastille modifies,
160# with permissions intact
161#
162##############################################################################
163
164sub getIPFLocation () { # Temporary until we get defined search space support
165 my $ipf=&getGlobal('BIN','ipf_new');
166 my $ipfstat=&getGlobal('BIN','ipfstat_new');
167 if (not(-e $ipf)) { # Detect if the binaries moved
168 $ipf = &getGlobal('BIN','ipf');
169 $ipfstat=&getGlobal('BIN','ipfstat');
170 }
171 return ($ipf, $ipfstat);
172}
173
174##############################################
175# Given a combination of service results, provided
176# in an array, this function combines the result into
177# a reasonable aggregate result
178##############################################
179
180sub B_combine_service_results(@){
181 my @results = @_;
182
183 #TODO: Consider greater sophistication wrt inconsistent, or not installed.
184
185 foreach my $result (@results) {
186 if (not(($result == SECURE_CAN_CHANGE) or
187 ($result == SECURE_CANT_CHANGE) or
188 ($result == NOT_INSTALLED()))) {
189 return NOTSECURE_CAN_CHANGE();
190 }
191 }
192 return SECURE_CANT_CHANGE();
193}
194
195####################################################################
196# &getGlobalSwlist ($file);
197# This function returns the product and fileset information for
198# a given file or directory if it exists in the IPD otherwise
199# it returns undefined "undef"
200#
201# uses $GLOBAL_SWLIST{"$FILE"}
202####################################################################
203sub getGlobalSwlist($){
204 no strict;
205 my $file = $_[0];
206
207
208 if(! %GLOBAL_SWLIST) {
209 # Generating swlist database for swmodify changes that will be required
210 # The database will be a hash of fully qualified file names that reference
211 # the files product name and fileset. These values are required to use
212 # swmodify...
213
214 # Files tagged 'is_volatile' in the IPD are not entered in the swlist database
215 # in order to avoid invoking swmodify if the file is changed later. Attempting to
216 # swmodify 'volatile' files is both unneccessary and complicated since swverify will
217 # not evaluate volatile files anyway, and adding another value to the swlist database
218 # would require complex code changes.
219
220 # temp variable to keep swlist command /usr/sbin/swlist
221 my $swlist = &getGlobal('BIN',"swlist");
222
223 # listing of each directory and file that was installed by SD on the target machine
224 my @fileList = `$swlist -a is_volatile -l file`;
225
226 # listing of each patch and the patches that supersede each.
227 # hash which is indexed by patch.fileset on the system
228 my %patchSuperseded;
229
230 my @patchList = `${swlist} -l fileset -a superseded_by *.*,c=patch 2>&1`;
231 # check to see if any patches are present on the system
232 if(($? >> 8) == 0) {
233
234 # determining patch suppression for swmodify.
235 foreach my $patchState (@patchList) {
236 # removing empty lines and commented lines.
237 if($patchState !~ /^\s*\#/ && $patchState !~ /^\s*$/) {
238
239 # removing leading white space
240 $patchState =~ s/^\s+//;
241 my @patches = split /\s+/, $patchState;
242 if($#patches == 0){
243 # patch is not superseded
244 $patchSuperseded{$patches[0]} = 0;
245 }
246 else {
247 # patch is superseded
248 $patchSuperseded{$patches[0]} = 1;
249 }
250 }
251 }
252 }
253 else {
254 &B_log("DEBUG","No patches found on the system.\n");
255 }
256
257 if($#fileList >= 0){
258 # foreach line of swlist output
259 foreach my $fileEntry ( @fileList ){
260 #filter out commented portions
261 if( $fileEntry !~ /^\s*\#/ ){
262 chomp $fileEntry;
263 # split the output into three fields: product.fileset, filename, flag_isvolatile
264 my( $productInfo, $file, $is_volatile ) = $fileEntry =~ /^\s*(\S+): (\S+)\t(\S+)/ ;
265 # do not register volatile files
266 next if ($is_volatile =~ /true/); # skip to next file entry
267 $productInfo =~ s/\s+//;
268 $file =~ s/\s+//;
269 # if the product is a patch
270 if($productInfo =~ /PH(CO|KL|NE|SS)/){
271 # if the patch is not superseded by another patch
272 if($patchSuperseded{$productInfo} == 0){
273 # add the patch to the list of owner for this file
274 push @{$GLOBAL_SWLIST{"$file"}}, $productInfo;
275 }
276 }
277 # not a patch.
278 else {
279 # add the product to the list of owners for this file
280 push @{$GLOBAL_SWLIST{"$file"}}, $productInfo;
281 }
282
283 }
284 }
285 }
286 else{
287 # defining GLOBAL_SWLIST in error state.
288 $GLOBAL_SWLIST{"ERROR"} = "ERROR";
289 &B_log("ERROR","Could not execute swlist. Swmodifys will not be attempted");
290 }
291 }
292
293 if(exists $GLOBAL_SWLIST{"$file"}){
294 return $GLOBAL_SWLIST{"$file"};
295 }
296 else {
297 return undef;
298 }
299}
300
301###################################################################
302# &B_check_system;
303# This subroutine is called to validate that bastille may be
304# safely run on the current system. It will check to insure
305# that there is enough file system space, mounts are rw, nfs
306# mounts are not mounted noroot, and swinstall, swremove and
307# swmodify are not running
308#
309# uses ErrorLog
310#
311##################################################################
312sub B_check_system {
313 # exitFlag is one if a conflict with the successful execution
314 # of bastille is found.
315 my $exitFlag = 0;
316
317 my $ignoreCheck = &getGlobal("BDIR","config") . "/.no_system_check";
318 if( -e $ignoreCheck ) {
319 return $exitFlag;
320 }
321
322 # first check for swinstall, swmodify, or swremove processes
323 my $ps = &getGlobal('BIN',"ps") . " -el";
324 my @processTable = `$ps`;
325 foreach my $process (@processTable) {
326 if($process =~ /swinstall/ ) {
327 &B_log("ERROR","Bastille cannot run while a swinstall is in progress.\n" .
328 "Complete the swinstall operation and then run Bastille.\n\n");
329 $exitFlag = 1;
330 }
331
332 if($process =~ /swremove/ ) {
333 &B_log("ERROR","Bastille cannot run while a swremove is in progress.\n" .
334 "Complete the swremove operation and then run Bastille.\n\n");
335 $exitFlag = 1;
336 }
337
338 if($process =~ /swmodify/ ) {
339 &B_log("ERROR","Bastille cannot run while a swmodify is in progress.\n" .
340 "Complete the swmodify operation and then run Bastille.\n\n");
341 $exitFlag = 1;
342 }
343
344 }
345
346 # check for root read only mounts for /var /etc /stand /
347 # Bastille is required to make changes to these file systems.
348 my $mount = &getGlobal('BIN',"mount");
349 my $rm = &getGlobal('BIN',"rm");
350 my $touch = &getGlobal('BIN',"touch");
351
352 my @mnttab = `$mount`;
353
354 if(($? >> 8) != 0) {
355 &B_log("WARNING","Unable to use $mount to determine if needed partitions\n" .
356 "are root writable, based on disk mount options.\n" .
357 "Bastille will continue but note that disk\n" .
358 "mount checks were skipped.\n\n");
359 }
360 else {
361 foreach my $record (@mnttab) {
362 my @fields = split /\s+/, $record;
363 if ((defined $fields[0]) && (defined $fields[2]) && (defined $fields[3])) {
364 my $mountPoint = $fields[0];
365 my $mountType = $fields[2];
366 my $mountOptions = $fields[3];
367
368 # checks for /stand and /var/* removed
369 if($mountPoint =~ /^\/$|^\/etc|^\/var$/) {
370
371 if($mountOptions =~ /^ro,|,ro,|,ro$/) {
372 &B_log("ERROR","$mountPoint is mounted read-only. Bastille needs to make\n" .
373 "modifications to this file system. Please remount\n" .
374 "$mountPoint read-write and then run Bastille again.\n\n");
375 $exitFlag = 1;
376 }
377 # looking for an nfs mounted file system
378 if($mountType =~/.+:\//){
379 my $fileExisted=0;
380 if(-e "$mountPoint/.bastille") {
381 $fileExisted=1;
382 }
383
384 `$touch $mountPoint/.bastille 1>/dev/null 2>&1`;
385
386 if( (! -e "$mountPoint/.bastille") || (($? >> 8) != 0) ) {
387 &B_log("ERROR","$mountPoint is an nfs mounted file system that does\n" .
388 "not allow root to write to. Bastille needs to make\n" .
389 "modifications to this file system. Please remount\n" .
390 "$mountPoint giving root access and then run Bastille\n" .
391 "again.\n\n");
392
393 $exitFlag = 1;
394 }
395 # if the file did not exist befor the touch then remove the generated file
396 if(! $fileExisted) {
397 `$rm -f $mountPoint/.bastille 1>/dev/null 2>&1`;
398 }
399 }
400 }
401 }
402 else {
403 &B_log("WARNING","Unable to use $mount to determine if needed partitions\n" .
404 "are root writable, based on disk mount options.\n" .
405 "Bastille will continue but note that disk\n" .
406 "mount checks were skipped.\n\n");
407 }
408 }
409
410 }
411
412 # checks for enough disk space in directories that Bastille writes to.
413 my $bdf = &getGlobal('BIN',"bdf");
414 #directories that Bastille writes to => required space in kilobytes.
415 my %bastilleDirs = ( "/etc/opt/sec_mgmt/bastille" => "4", "/var/opt/sec_mgmt/bastille"=> "1000");
416 for my $directory (sort keys %bastilleDirs) {
417 my @diskUsage = `$bdf $directory`;
418
419 if(($? >> 8) != 0) {
420 &B_log("WARNING","Unable to use $bdf to determine disk usage for\n" .
421 "$directory\n" .
422 "Bastille will continue but note that disk\n" .
423 "usage checks were skipped.\n\n");
424
425 }
426 else {
427 # removing bdf header line from usage information.
428 shift @diskUsage;
429 my $usageString= "";
430
431 foreach my $usageRecord (@diskUsage) {
432 chomp $usageRecord;
433 $usageString .= $usageRecord;
434 }
435
436 $usageString =~ s/^\s+//;
437
438 my @fields = split /\s+/, $usageString;
439 if($#fields != 5) {
440 &B_log("WARNING","Unable to use $bdf to determine disk usage for\n" .
441 "$directory\n" .
442 "Bastille will continue but note that disk\n" .
443 "usage checks were skipped.\n\n");
444 }
445 else {
446
447 my $mountPoint = $fields[5];
448 my $diskAvail = $fields[3];
449
450 if($diskAvail <= $bastilleDirs{"$directory"}) {
451 &B_log("ERROR","$mountPoint does not contain enough available space\n" .
452 "for Bastille to run properly. $directory needs\n" .
453 "at least $bastilleDirs{$directory} kilobytes of space.\n" .
454 "Please clear at least that amount of space from\n" .
455 "$mountPoint and run Bastille again.\n" .
456 "Current Free Space available = ${diskAvail} k\n\n");
457 $exitFlag = 1;
458 }
459 }
460 }
461 }
462
463 # check to make sure that we are in at least run level 2 before we attempt to run
464 my $who = &getGlobal('BIN', "who") . " -r";
465 my $levelInfo = `$who`;
466 if(($? >> 8) != 0 ) {
467 &B_log("WARNING","Unable to use \"$who\" to determine system run.\n" .
468 "level Bastille will continue but note that the run\n" .
469 "level check was skipped.\n\n");
470 }
471 else {
472 chomp $levelInfo;
473 my @runlevel = split /\s+/, $levelInfo;
474 if ((! defined $runlevel[3]) or ($runlevel[3] < 2)) {
475 &B_log("WARNING","Bastille requires a run-level of 2 or more to run properly.\n" .
476 "Please move your system to a higher run level and then\n" .
477 "run 'bastille -b'.\n\n");
478 if(defined $runlevel[3]) {
479 &B_log("ERROR","Current run-level is '$runlevel[3]'.\n\n");
480 $exitFlag=1;
481 }
482 else {
483 &B_log("WARNING","Unable to use \"$who\" to determine system run.\n" .
484 "level Bastille will continue but note that the run\n" .
485 "level check was skipped.\n\n");
486 }
487 }
488 else {
489 &B_log("DEBUG","System run-level is $runlevel[3]\n");
490 }
491 }
492
493 if($exitFlag) {
494 exit(1);
495 }
496
497}
498
499###################################################################
500# &B_swmodify($file);
501# This subroutine is called after a file is modified. It will
502# redefine the file in the IPD with it's new properties. If
503# the file is not in the IPD it does nothing.
504#
505# uses B_System to make the swmodifications.
506##################################################################
507sub B_swmodify($){
508 my $file = $_[0];
509 if(defined &getGlobalSwlist($file)){
510 my $swmodify = &getGlobal('BIN',"swmodify");
511 my @productsInfo = @{&getGlobalSwlist($file)};
512 # running swmodify on files that were altered by this function but
513 # were created and maintained by SD
514 foreach my $productInfo (@productsInfo) {
515 &B_System("$swmodify -x files='$file' $productInfo",
516 "$swmodify -x files='$file' $productInfo");
517 }
518 }
519}
520
521####################################################################
522# &B_load_ipf_rules($ipfruleset);
523# This function enables an ipfruleset. It's a little more
524# specific than most API functions, but necessary because
525# ipf doesn't return correct exit codes (syntax error results
526# in a 0 exit code)
527#
528# uses ActionLog and ErrorLog to log
529# calls crontab directly (to list and to read in new jobs)
530###################################################################
531sub B_load_ipf_rules ($) {
532 my $ipfruleset=$_[0];
533
534 &B_log("DEBUG","# sub B_load_ipf_rules");
535
536 # TODO: grab ipf.conf dynamically from the rc.config.d files
537 my $ipfconf = &getGlobal('FILE','ipf.conf');
538
539 # file system changes - these are straightforward, and the API
540 # will take care of the revert
541 &B_create_file($ipfconf);
542 &B_blank_file($ipfconf, 'a$b');
543 &B_append_line($ipfconf, 'a$b', $ipfruleset);
544
545 # runtime changes
546
547 # define binaries
548 my $grep = &getGlobal('BIN', 'grep');
549 my ($ipf, $ipfstat) = &getIPFLocation;
550 # create backup rules
551 # This will exit with a non-zero exit code because of the grep
552 my @oldrules = `$ipfstat -io 2>&1 | $grep -v empty`;
553
554 my @errors=`$ipf -I -Fa -f $ipfconf 2>&1`;
555
556 if(($? >> 8) == 0) {
557
558 &B_set_rc("IPF_START","1");
559 &B_set_rc("IPF_CONF","$ipfconf");
560
561 # swap the rules in
562 &B_System("$ipf -s","$ipf -s");
563
564 # now create a "here" document with the previous version of
565 # the rules and put it into the revert-actions script
566 &B_revert_log("$ipf -I -Fa -f - <<EOF\n@{oldrules}EOF");
567
568 if (@errors) {
569 &B_log("ERROR","ipfilter produced the following errors when\n" .
570 " loading $ipfconf. You probably had an invalid\n" .
571 " rule in ". &getGlobal('FILE','customipfrules') ."\n".
572 "@errors\n");
573 }
574
575 } else {
576 &B_log("ERROR","Unable to run $ipf\n");
577 }
578
579}
580
581
582
583####################################################################
584# &B_Schedule($pattern,$cronjob);
585# This function schedules a cronjob. If $pattern exists in the
586# crontab file, that job will be replaced. Otherwise, the job
587# will be appended.
588#
589# uses ActionLog and ErrorLog to log
590# calls crontab directly (to list and to read in new jobs)
591###################################################################
592sub B_Schedule ($$) {
593 my ($pattern,$cronjob)=@_;
594 $cronjob .= "\n";
595
596 &B_log("DEBUG","# sub B_Schedule");
597 my $crontab = &getGlobal('BIN','crontab');
598
599 my @oldjobs = `$crontab -l 2>/dev/null`;
600 my @newjobs;
601 my $patternfound=0;
602
603 foreach my $oldjob (@oldjobs) {
604 if (($oldjob =~ m/$pattern/ ) and (not($patternfound))) {
605 push @newjobs, $cronjob;
606 $patternfound=1;
607 &B_log("ACTION","changing existing cron job which matches $pattern with\n" .
608 "$cronjob");
609 } elsif ($oldjob !~ m/$pattern/ ) {
610 &B_log("ACTION","keeping existing cron job $oldjob");
611 push @newjobs, $oldjob;
612 } #implied: else if pattern matches, but we've
613 #already replaced one, then toss the others.
614 }
615
616 unless ($patternfound) {
617 &B_log("ACTION","adding cron job\n$cronjob\n");
618 push @newjobs, $cronjob;
619 }
620
621 if(open(CRONTAB, "|$crontab - 2> /dev/null")) {
622 print CRONTAB @newjobs;
623
624 # now create a "here" document with the previous version of
625 # the crontab file and put it into the revert-actions script
626 &B_revert_log("$crontab <<EOF\n" . "@oldjobs" . "EOF");
627 close CRONTAB;
628 }
629
630 # Now check to make sure it happened, since cron will exit happily
631 # (retval 0) with no changes if there are any syntax errors
632 my @editedjobs = `$crontab -l 2>/dev/null`;
633
634 if (@editedjobs ne @newjobs) {
635 &B_log("ERROR","failed to add cron job:\n$cronjob\n" .
636 " You probably had an invalid crontab file to start with.");
637 }
638
639}
640
641
642#This function turns off a service, given a service name defined in HP-UX.service
643
644sub B_ch_rc($) {
645
646 my ($service_name)=@_;
647
648 if (&GetDistro != "^HP-UX") {
649 &B_log("ERROR","Tried to call ch_rc $service_name on a non-HP-UX\n".
650 " system! Internal Bastille error.");
651 return undef;
652 }
653 my $configfile="";
654 my $command = &getGlobal('BIN', 'ch_rc');
655
656 my $startup_script=&getGlobal('DIR','initd') . "/". $service_name;
657 my @rc_parameters= @{ &getGlobal('SERVICE',$service_name) };
658 my @rcFiles=@{ &getGlobal('RCCONFIG',$service_name) };
659 my $rcFile='';
660 if (@rcFiles == 1){
661 $rcFile=$rcFiles[0];
662 } else {
663 &B_log("FATAL","Multiple RC Files not yet supported... internal error.");
664 }
665
666 # if the service-related process is not run, and the control variable is stilll 1
667 # there is a inconsistency. in this case we only need to change the control variable
668 my @psnames=@{ &getGlobal('PROCESS',$service_name)};
669 my @processes;
670 foreach my $psname (@psnames) {
671 $psname .= '\b'; # avoid embedded match; anchor search pattern to trailing word boundry
672 my @procList = &isProcessRunning($psname);
673 if(@procList >= 0){
674 splice @processes,$#processes+1,0,@procList;
675 }
676 }
677#Actually set the rc variable
678 foreach my $rcVariable (@rc_parameters){
679 my $orig_value = &B_get_rc($rcVariable);
680 if ($orig_value eq "" ) { #If variable not set, used the defined file
681 $configfile=&getGlobal("DIR","rc.config.d") . "/" . $rcFile;
682 if (not( -f $configfile )) {
683 &B_create_file($configfile);
684 }
685 }
686 &B_log("DEBUG","In B_ch_rc (no procs), setting $rcVariable to 0 in $configfile" .
687 ", with an original value of $orig_value with rcfile: $rcFile");
688 if ( ! @processes) { # IF there are no processes we don't neet to perform a "stop"
689 &B_set_rc($rcVariable, "0", $configfile);
690 } else {
691 if ( $orig_value !~ "1" ) { #If param is not already 1, the "stop" script won't work
692 &B_set_rc($rcVariable, "1",$configfile);
693 }
694 &B_System ($startup_script . " stop", #stop service, then restart if the user runs bastille -r
695 $startup_script . " start");
696 # set parameter, so that service will stay off after reboots
697 &B_set_rc($rcVariable, "0", $configfile);
698 }
699 }
700}
701
702
703# This routine sets a value in a given file
704sub B_set_value($$$) {
705 my ($param, $value, $file)=@_;
706
707 &B_log("DEBUG","B_set_value: $param, $value, $file");
708 if (! -e $file ) {
709 &B_create_file("$file");
710 }
711
712 # If a value is already set to something other than $value then reset it.
713 #Note that though this tests for "$value ="the whole line gets replaced, so
714 #any pre-existing values are also replaced.
715 &B_replace_line($file,"^$param\\s*=\\s*","$param=$value\n");
716 # If the value is not already set to something then set it.
717 &B_append_line($file,"^$param\\s*=\\s*$value","$param=$value\n");
718
719}
720
721
722##################################################################################
723# &B_chperm($owner,$group,$mode,$filename(s))
724# This function changes ownership and mode of a list of files. Takes four
725# arguments first the owner next the group and third the new mode in oct and
726# last a list of files that the permissions changes should take affect on.
727#
728# uses: &swmodify and &B_revert_log
729##################################################################################
730sub B_chperm($$$$) {
731 my ($newown, $newgrp, $newmode, $file_expr) = @_;
732 my @files = glob($file_expr);
733
734 my $return = 1;
735
736 foreach my $file (@files){
737 my @filestat = stat $file;
738 my $oldmode = (($filestat[2]/512) % 8) .
739 (($filestat[2]/64) % 8) .
740 (($filestat[2]/8) % 8) .
741 (($filestat[2]) % 8);
742
743 if((chown $newown, $newgrp, $file) != 1 ){
744 &B_log("ERROR","Could not change ownership of $file to $newown:$newgrp\n");
745 $return = 0;
746 }
747 else{
748 &B_log("ACTION","Changed ownership of $file to $newown:$newgrp\n");
749 # swmodifying file if possible...
750 &B_swmodify($file);
751 &B_revert_log(&getGlobal('BIN',"chown") . " $filestat[4]:$filestat[5] $file\n");
752 }
753
754 my $newmode_formatted=sprintf "%5lo",$newmode;
755
756 if((chmod $newmode, $file) != 1){
757 &B_log("ERROR","Could not change mode of $file to $newmode_formatted\n");
758 $return = 0;
759 }
760 else{
761 &B_log("ACTION","Changed mode of $file to $newmode_formatted\n");
762 &B_revert_log(&getGlobal('BIN',"chmod") . " $oldmode $file\n");
763 }
764
765
766 }
767 return $return;
768}
769
770############################################################################
771# &B_install_jail($jailname, $jailconfigfile);
772# This function takes two arguments ( jail_name, jail_config )
773# It's purpose is to take read in config files that define a
774# chroot jail and then generate it bases on that specification
775############################################################################
776sub B_install_jail($$) {
777
778 my $jailName = $_[0]; # Name of the jail e.g bind
779 my $jailConfig = $_[1]; # Name of the jails configuration file
780 # create the root directory of the jail if it does not exist
781 &B_create_dir( &getGlobal('BDIR','jail'));
782 &B_chperm(0,0,0555,&getGlobal('BDIR','jail'));
783
784 # create the Jail dir if it does not exist
785 &B_create_dir( &getGlobal('BDIR','jail') . "/" . $jailName);
786 &B_chperm(0,0,0555,&getGlobal('BDIR','jail') . "/". $jailName);
787
788
789 my $jailPath = &getGlobal('BDIR','jail') . "/" . $jailName;
790 my @lines; # used to store no commented no empty config file lines
791 # open configuration file for desired jail and parse in commands
792 if(open(JAILCONFIG,"< $jailConfig")) {
793 while(my $line=<JAILCONFIG>){
794 if($line !~ /^\s*\#|^\s*$/){
795 chomp $line;
796 push(@lines,$line);
797 }
798 }
799 close JAILCONFIG;
800 }
801 else{
802 &B_log("ERROR","Open Failed on filename: $jailConfig\n");
803 return 0;
804 }
805 # read through commands and execute
806 foreach my $line (@lines){
807 &B_log("ACTION","Install jail: $line\n");
808 my @confCmd = split /\s+/,$line;
809 if($confCmd[0] =~ /dir/){ # if the command say to add a directory
810 if($#confCmd == 4) { # checking dir Cmd form
811 if(! (-d $jailPath . "/" . $confCmd[1])){
812 #add a directory and change its permissions according
813 #to the conf file
814 &B_create_dir( $jailPath . "/" . $confCmd[1]);
815 &B_chperm((getpwnam($confCmd[3]))[2],
816 (getgrnam($confCmd[4]))[2],
817 oct($confCmd[2]),
818 $jailPath . "/" . $confCmd[1]);
819 }
820 }
821 else {
822 &B_log("ERROR","Badly Formed Configuration Line:\n$line\n\n");
823 }
824 }
825 elsif($confCmd[0] =~ /file/) {
826 if($#confCmd == 5) { # checking file cmd form
827 if(&B_cp($confCmd[1],$jailPath . "/" . $confCmd[2])){
828 # for copy command cp file and change perms
829 &B_chperm($confCmd[4],$confCmd[5],oct($confCmd[3]),$jailPath . "/" . $confCmd[2]);
830 }
831 else {
832 &B_log("ERROR","Could not complete copy on specified files:\n" .
833 "$line\n");
834 }
835 }
836 else {
837 &B_log("ERROR","Badly Formed Configuration Line:\n" .
838 "$line\n\n");
839 }
840 }
841 elsif($confCmd[0] =~ /slink/) {
842 if($#confCmd == 2) { # checking file cmd form
843 if(!(-e $jailPath . "/" . $confCmd[2])){
844 #for symlink command create the symlink
845 &B_symlink($jailPath . "/" . $confCmd[1], $confCmd[2]);
846 }
847 }
848 else {
849 &B_log("ERROR","Badly Formed Configuration Line:\n" .
850 "$line\n\n");
851 }
852 }
853 else {
854 &B_log("ERROR","Unrecognized Configuration Line:\n" .
855 "$line\n\n");
856 }
857 }
858 return 1;
859}
860
861
862
863###########################################################################
864# &B_list_processes($service) #
865# #
866# This subroutine uses the GLOBAL_PROCESS hash to determine if a #
867# service's corresponding processes are running on the system. #
868# If any of the processes are found to be running then the process #
869# name(s) is/are returned by this subroutine in the form of an list #
870# If none of the processes that correspond to the service are running #
871# then an empty list is returned. #
872###########################################################################
873sub B_list_processes($) {
874
875 # service name
876 my $service = $_[0];
877 # list of processes related to the service
878 my @processes=@{ &getGlobal('PROCESS',$service)};
879
880 # current systems process information
881 my $ps = &getGlobal('BIN',"ps");
882 my $psTable = `$ps -elf`;
883
884 # the list to be returned from the function
885 my @running_processes;
886
887 # for every process associated with the service
888 foreach my $process (@processes) {
889 # if the process is in the process table then
890 if($psTable =~ m/$process/) {
891 # add the process to the list, which will be returned
892 push @running_processes, $process;
893 }
894
895 }
896
897 # return the list of running processes
898 return @running_processes;
899
900}
901
902#############################################################################
903# &B_list_full_processes($service) #
904# #
905# This subroutine simply grep through the process table for those matching #
906# the input argument TODO: Allow B_list process to levereage this code #
907# ... Not done this cycle to avoid release risk (late in cycle) #
908#############################################################################
909sub B_list_full_processes($) {
910
911 # service name
912 my $procName = $_[0];
913 my $ps = &getGlobal('BIN',"ps");
914 my @psTable = split(/\n/,`$ps -elf`);
915
916 # for every process associated with the service
917 my @runningProcessLines = grep(/$procName/ , @psTable);
918 # return the list of running processes
919 return @runningProcessLines;
920}
921
922################################################################################
923# &B_deactivate_inetd_service($service); #
924# #
925# This subroutine will disable all inetd services associated with the input #
926# service name. Service name must be a reference to the following hashes #
927# GLOBAL_SERVICE GLOBAL_SERVTYPE and GLOBAL_PROCESSES. If processes are left #
928# running it will note these services in the TODO list as well as instruct the#
929# user in how they remaining processes can be disabled. #
930################################################################################
931sub B_deactivate_inetd_service($) {
932 my $service = $_[0];
933 my $servtype = &getGlobal('SERVTYPE',"$service");
934 my $inetd_conf = &getGlobal('FILE',"inetd.conf");
935
936 # check the service type to ensure that it can be configured by this subroutine.
937 if($servtype ne 'inet') {
938 &B_log("ACTION","The service \"$service\" is not an inet service so it cannot be\n" .
939 "configured by this subroutine\n");
940 return 0;
941 }
942
943 # check for the inetd configuration files existence so it may be configured by
944 # this subroutine.
945 if(! -e $inetd_conf ) {
946 &B_log("ACTION","The file \"$inetd_conf\" cannot be located.\n" .
947 "Unable to configure inetd\n");
948 return 0;
949 }
950
951 # list of service identifiers present in inetd.conf file.
952 my @inetd_entries = @{ &getGlobal('SERVICE',"$service") };
953
954 foreach my $inetd_entry (@inetd_entries) {
955 &B_hash_comment_line($inetd_conf, "^\\s*$inetd_entry");
956 }
957
958 # list of processes associated with this service which are still running
959 # on the system
960 my @running_processes = &B_list_processes($service);
961
962 if($#running_processes >= 0) {
963 my $todoString = "\n" .
964 "---------------------------------------\n" .
965 "Deactivating Inetd Service: $service\n" .
966 "---------------------------------------\n" .
967 "The following process(es) are associated with the inetd service \"$service\".\n" .
968 "They are most likely associated with a session which was initiated prior to\n" .
969 "running Bastille. To disable a process see \"kill(1)\" man pages or reboot\n" .
970 "the system\n" .
971 "Active Processes:\n" .
972 "###################################\n";
973 foreach my $running_process (@running_processes) {
974 $todoString .= "\t$running_process\n";
975 }
976 $todoString .= "###################################\n";
977
978 &B_TODO($todoString);
979 }
980
981}
982
983
984################################################################################
985# B_get_rc($key); #
986# #
987# This subroutine will use the ch_rc binary to get rc.config.d variables #
988# values properly escaped and quoted. #
989################################################################################
990sub B_get_rc($) {
991
992 my $key=$_[0];
993 my $ch_rc = &getGlobal('BIN',"ch_rc");
994
995 # get the current value of the given parameter.
996 my $currentValue=`$ch_rc -l -p $key`;
997 chomp $currentValue;
998
999 if(($? >> 8) == 0 ) {
1000 # escape all meta characters.
1001 # $currentValue =~ s/([\"\`\$\\])/\\$1/g;
1002 # $currentValue = '"' . $currentValue . '"';
1003 }
1004 else {
1005 return undef;
1006 }
1007
1008 return $currentValue;
1009}
1010
1011
1012
1013################################################################################
1014# B_set_rc($key,$value); #
1015# #
1016# This subroutine will use the ch_rc binary to set rc.config.d variables. As #
1017# well as setting the variable this subroutine will set revert strings. #
1018# #
1019################################################################################
1020sub B_set_rc($$;$) {
1021
1022 my ($key,$value,$configfile)=@_;
1023 my $ch_rc = &getGlobal('BIN',"ch_rc");
1024
1025 # get the current value of the given parameter.
1026 my $currentValue=&B_get_rc($key);
1027 if(defined $currentValue ) {
1028 if ($currentValue =~ /^\"(.*)\"$/ ) {
1029 $currentValue = '"\"' . $1 . '\""';
1030 }
1031 if ($value =~ /^\"(.*)\"$/ ) {
1032 $value = '"\"' . $1 . '\""';
1033 }
1034 if ( &B_System("$ch_rc -a -p $key=$value $configfile",
1035 "$ch_rc -a -p $key=$currentValue $configfile") ) {
1036 #ch_rc success
1037 return 1;
1038 }
1039 else {
1040 #ch_rc failure.
1041 return 0;
1042 }
1043 }
1044 else {
1045 &B_log("ERROR","ch_rc was unable to lookup $key\n");
1046 return 0;
1047 }
1048
1049}
1050
1051
1052################################################################################
1053# &ChrootHPApache($chrootScript,$httpd_conf,$httpd_bin,
1054# $apachectl,$apacheJailDir,$serverString);
1055#
1056# This subroutine given an chroot script, supplied by the vendor, a
1057# httpd.conf file, the binary location of httpd, the control script,
1058# the jail directory, and the servers identification string, descriptive
1059# string for TODO etc. It makes modifications to httpd.conf so that when
1060# Apache starts it will chroot itself into the jail that the above
1061# mentions script creates.
1062#
1063# uses B_replace_line B_create_dir B_System B_TODO
1064#
1065###############################################################################
1066sub B_chrootHPapache($$$$$$) {
1067
1068 my ($chrootScript,$httpd_conf,$httpd_bin,$apachectl,$apacheJailDir,$serverString)= @_;
1069
1070 my $exportpath = "export PATH=/usr/bin;";
1071 my $ps = &getGlobal('BIN',"ps");
1072 my $isRunning = 0;
1073 my $todo_header = 0;
1074
1075 # checking for a 2.0 version of the apache chroot script.
1076 if(-e $chrootScript ) {
1077
1078 if(open HTTPD, $httpd_conf) {
1079 while (my $line = <HTTPD>){
1080 if($line =~ /^\s*Chroot/) {
1081 &B_log("DEBUG","Apache is already running in a chroot as specified by the following line:\n$line\n" .
1082 "which appears in the httpd.conf file. No Apache Chroot action was taken.\n");
1083 return;
1084 }
1085 }
1086 close(HTTPD);
1087 }
1088
1089 if(`$ps -ef` =~ $httpd_bin ) {
1090 $isRunning=1;
1091 &B_System("$exportpath " . $apachectl . " stop","$exportpath " . $apachectl . " start");
1092 }
1093 &B_replace_line($httpd_conf, '^\s*#\s*Chroot' ,
1094 "Chroot " . $apacheJailDir);
1095 if(-d &getGlobal('BDIR',"jail")){
1096 &B_log("DEBUG","Jail directory already exists. No action taken.\n");
1097 }
1098 else{
1099 &B_log("ACTION","Jail directory was created.\n");
1100 &B_create_dir( &getGlobal('BDIR','jail'));
1101 }
1102
1103 if(-d $apacheJailDir){
1104 &B_log("DEBUG","$serverString jail already exists. No action taken.\n");
1105 }
1106 else{
1107 &B_System(&getGlobal('BIN',"umask") . " 022; $exportpath " . $chrootScript,
1108 &getGlobal('BIN',"echo") . " \"Your $serverString is now running outside of it's\\n" .
1109 "chroot jail. You must manually migrate your web applications\\n" .
1110 "back to your Apache server's httpd.conf defined location(s).\\n".
1111 "After you have completed this, feel free to remove the jail directories\\n" .
1112 "from your machine. Your apache jail directory is located in\\n" .
1113 &getGlobal('BDIR',"jail") . "\\n\" >> " . &getGlobal('BFILE',"TOREVERT"));
1114
1115 }
1116 if($isRunning){
1117 &B_System("$exportpath " . $apachectl . " start","$exportpath " . $apachectl . " stop");
1118 &B_log("ACTION","$serverString is now running in an chroot jail.\n");
1119 }
1120
1121 &B_log("ACTION","The jail is located in " . $apacheJailDir . "\n");
1122
1123 if ($todo_header !=1){
1124 &B_TODO("\n---------------------------------\nApache Chroot:\n" .
1125 "---------------------------------\n");
1126 }
1127 &B_TODO("$serverString Chroot Jail:\n" .
1128 "httpd.conf contains the Apache dependencies. You should\n" .
1129 "review this file to ensure that the dependencies made it\n" .
1130 "into the jail. Otherwise, you run a risk of your Apache server\n" .
1131 "not having access to all its modules and functionality.\n");
1132
1133
1134 }
1135
1136}
1137
1138
1139sub isSystemTrusted {
1140 my $getprdef = &getGlobal('BIN',"getprdef");
1141 my $definition = &B_Backtick("$getprdef -t 2>&1");
1142 if($definition =~ "System is not trusted.") {
1143 return 0;
1144 } else {
1145 return 1;
1146 }
1147}
1148
1149
1150sub isTrustedMigrationAvailable {
1151 my $distroVersion='';
1152
1153 if (&GetDistro =~ '^HP-UX11.(\d*)') {
1154 $distroVersion=$1;
1155 if ($distroVersion < 23) { # Not available before 11.23
1156 return 0; #FALSE
1157 } elsif ($distroVersion >= 31) { #Bundled with 11.31 and after
1158 &B_log('DEBUG','isTrustedMigrationAvailable: HP-UX 11.31 always has trusted mode extensions');
1159 return 1;
1160 } elsif ($distroVersion == 23) { # Optional on 11.23 if filesets installed
1161 if ( -x &getGlobal('BIN',"userdbget") ) {
1162 &B_log('DEBUG','isTrustedMigrationAvailable: Trusted Extensions Installed');
1163 return 1;
1164 } else {
1165 &B_log('DEBUG','isTrustedMigrationAvailable: Trusted Extensions Not Installed');
1166 return 0; #FALSE
1167 }
1168 } else {
1169 &B_log('DEBUG','isTrustedMigrationAvailable: ' . &GetDistro .
1170 ' not currently supported for trusted extentions.');
1171 return 0; #FALSE
1172 }
1173 } else {
1174 &B_log('WARNING','isTrustedMigrationAvailable: HP-UX routine called on Linux system');
1175 return 0; #FALSE
1176 }
1177}
1178
1179
1180
1181###########################################################################
1182# &checkServiceOnHPUX($service);
1183#
1184# Checks if the given service is running on an HP/UX system. This is
1185# called by B_is_Service_Off(), which is the function that Bastille
1186# modules should call.
1187#
1188# Return values:
1189# NOTSECURE_CAN_CHANGE() if the service is on
1190# SECURE_CANT_CHANGE() if the service is off
1191# INCONSISTENT() if the state of the service cannot be determined
1192# NOT_INSTALLED() if the s/w isn't insalled
1193#
1194###########################################################################
1195sub checkServiceOnHPUX($) {
1196 my $service=$_[0];
1197
1198 # get the list of parameters which could be used to initiate the service
1199 # (could be in /etc/rc.config.d, /etc/inetd.conf, or /etc/inittab, so we
1200 # check all of them)
1201 my @params= @{ &getGlobal('SERVICE',$service) };
1202 my $grep =&getGlobal('BIN', 'grep');
1203 my $inetd=&getGlobal('FILE', 'inetd.conf');
1204 my $inittab=&getGlobal('FILE', 'inittab');
1205 my $retVals;
1206 my $startup=&getGlobal('DIR','initd') ;
1207 my @inet_bins= @{ &getGlobal('PROCESS',$service) };
1208
1209 my $entry_found = 0;
1210
1211 &B_log("DEBUG","CheckHPUXservice: $service");
1212 my $full_initd_path = $startup . "/" . $service;
1213 if ($GLOBAL_SERVTYPE{$service} eq "rc") { # look for the init script in /sbin/init.d
1214 if (not(-e $full_initd_path )) {
1215 return NOT_INSTALLED();
1216 }
1217 } else { #inet-based service, so look for inetd.conf entries.
1218 &B_log("DEBUG","Checking inet service $service");
1219 my @inet_entries= @{ &getGlobal('SERVICE',$service) };
1220 foreach my $service (@inet_entries) {
1221 &B_log('DEBUG',"Checking for inetd.conf entry of $service in checkService on HPUX");
1222 my $service_regex = '^[#\s]*' . $service . '\s+';
1223 if ( &B_match_line($inetd, $service_regex) ) { # inet entry search
1224 &B_log('DEBUG',"$service present, entry exists");
1225 $entry_found = 1 ;
1226 }
1227 }
1228 if ($entry_found == 0 ) {
1229 return NOT_INSTALLED();
1230 }
1231 }
1232
1233 foreach my $param (@params) {
1234 &B_log("DEBUG","Checking to see if service $service is off.\n");
1235 if (&getGlobal('SERVTYPE', $service) =~ /rc/) {
1236 my $ch_rc=&getGlobal('BIN', 'ch_rc');
1237 my $on=&B_Backtick("$ch_rc -l -p $param");
1238
1239 $on =~ s/\s*\#.*$//; # remove end-of-line comments
1240 $on =~ s/^\s*\"(.+)\"\s*$/$1/; # remove surrounding double quotes
1241 $on =~ s/^\s*\'(.+)\'\s*$/$1/; # remove surrounding single quotes
1242 $on =~ s/^\s*\"(.+)\"\s*$/$1/; # just in case someone did '"blah blah"'
1243
1244 chomp $on;
1245 &B_log("DEBUG","ch_rc returned: $param=$on in checkServiceOnHPUX");
1246
1247 if ($on =~ /^\d+$/ && $on != 0) {
1248 # service is on
1249 &B_log("DEBUG","CheckService found $param service is set to \'on\' in scripts.");
1250 return NOTSECURE_CAN_CHANGE();
1251 }
1252 elsif($on =~ /^\s*$/) {
1253 # if the value returned is an empty string return
1254 # INCONSISTENT(), since we don't know what the hard-coded default is.
1255 return INCONSISTENT();
1256 }
1257 } else {
1258 # those files which rely on comments to determine what gets
1259 # turned on, such as inetd.conf and inittab
1260 my $inettabs=&B_Backtick("$grep -e '^[[:space:]]*$param' $inetd $inittab");
1261 if ($inettabs =~ /.+/) { # . matches anything except newlines
1262 # service is not off
1263 &B_log("DEBUG","Checking inetd.conf and inittab; found $inettabs");
1264 ########################### BREAK out, don't skip question
1265 return NOTSECURE_CAN_CHANGE();
1266 }
1267 }
1268 } # foreach $param
1269
1270 # boot-time parameters are not set; check processes
1271 # checkprocs for services returns INCONSISTENT() if a service is found
1272 # since a found-service is inconsistent with the above checks.
1273 B_log("DEBUG","Boot-Parameters not set, checking processes.");
1274 if (&runlevel < 2) { # Below runlevel 2, it is unlikely that
1275 #services will be running, so just check "on-disk" state
1276 &B_log("NOTE","Running during boot sequence, so skipping process checks");
1277 return SECURE_CANT_CHANGE();
1278 } else {
1279 return &checkProcsForService($service);
1280 }
1281}
1282
1283sub runlevel {
1284 my $who = &getGlobal("BIN", "who");
1285 my $runlevel = &B_Backtick("$who -r");
1286 if ($runlevel =~ s/.* run-level (\S).*/$1/) {
1287 &B_log("DEBUG","Runlevel is: $runlevel");
1288 return $runlevel;
1289 } else {
1290 &B_log("WARNING","Can not determine runlevel, assuming runlevel 3");
1291 &B_log("DEBUG","Runlevel command output: $runlevel");
1292 return "3"; #safer since the who command didn't work, we'll assume
1293 # runlevel 3 since that provides more checks.
1294 }
1295}
1296
1297#
1298# given a profile file, it will return a PATH array set by the file.
1299#
1300sub B_get_path($) {
1301 my $file = $_[0];
1302 my $sh = &getGlobal("BIN", "sh");
1303 # use (``)[0] is becuase, signal 0 maybe trapped which will produce some stdout
1304 my $path = (`$sh -c '. $file 1>/dev/null 2>&1 < /dev/null ; echo \$PATH'`)[0];
1305 my @path_arr = split(":", $path);
1306 my %tmp_path;
1307 my %path;
1308 for my $tmpdir (@path_arr) {
1309 chomp $tmpdir;
1310 if ($tmpdir ne "" && ! $tmp_path{$tmpdir}) {
1311 $tmp_path{$tmpdir}++;
1312 }
1313 }
1314 return keys %tmp_path;
1315}
1316
1317# Convert to trusted mode if it's not already
1318sub convertToTrusted {
1319 &B_log("DEBUG","# sub convertToTrusted \n");
1320 if( ! &isSystemTrusted) {
1321
1322 my ($ok, $message) = &isOKtoConvert;
1323
1324 my $ts_header="\n---------------------------------\nTrusted Systems:\n" .
1325 "---------------------------------\n";
1326
1327 if ($ok) {
1328 # actually do the conversion
1329 if(&B_System(&getGlobal('BIN','tsconvert'), &getGlobal('BIN','tsconvert') . " -r")){
1330 # adjust change times for user passwords to keep them valid
1331 # default is to expire them when converting to a trusted system,
1332 # which can be problematic, especially since some older versions of
1333 # SecureShell do not allow the user to change the password
1334 &B_System(&getGlobal('BIN','modprpw') . " -V", "");
1335
1336 my $getprdef = &getGlobal('BIN','getprdef');
1337 my $oldsettings = &B_Backtick("$getprdef -m lftm,exptm,mintm,expwarn,umaxlntr");
1338 $oldsettings =~ s/ //g;
1339
1340 # remove password lifetime and increasing login tries so they
1341 # don't lock themselves out of the system entirely.
1342 # set default expiration time and the like.
1343 my $newsettings="lftm=0,exptm=0,mintm=0,expwarn=0,umaxlntr=10";
1344
1345 &B_System(&getGlobal('BIN','modprdef') . " -m $newsettings",
1346 &getGlobal('BIN','modprdef') . " -m $oldsettings");
1347
1348 &B_TODO($ts_header .
1349 "Your system has been converted to a trusted system.\n" .
1350 "You should review the security settings available on a trusted system.\n".
1351 "$message");
1352
1353 # to get rid of "Cron: Your job did not contain a valid audit ID."
1354 # error, we re-read the crontab file after converting to trusted mode
1355 # Nothing is necessary in "revert" since we won't be in trusted mode
1356 # at that time.
1357 # crontab's errors can be spurious, and this will report an 'error'
1358 # of the crontab file is missing, so we send stderr to the bit bucket
1359 my $crontab = &getGlobal('BIN',"crontab");
1360 &B_System("$crontab -l 2>/dev/null | $crontab","");
1361 }
1362
1363 } else {
1364 &B_TODO($ts_header . $message);
1365 return 0; # not ok to convert, so we didn't
1366 }
1367 }
1368 else {
1369 &B_log("DEBUG","System is already in trusted mode, no action taken.\n");
1370 return 1;
1371 }
1372
1373 # just to make sure
1374 if( &isSystemTrusted ) {
1375 return 1;
1376 } else {
1377 &B_log("ERROR","Trusted system conversion was unsuccessful for an unknown reason.\n" .
1378 " You may try using SAM/SMH to do the conversion instead of Bastille.\n");
1379 return 0;
1380 }
1381}
1382
1383# isOKtoConvert - check for conflicts between current system state and trusted
1384# mode
1385#
1386# Return values
1387# 0 - conflict found, see message for details
1388# 1 - no conflicts, see message for further instructions
1389#
1390sub isOKtoConvert {
1391 &B_log("DEBUG","# sub isOKtoConvert \n");
1392 # initialize text for TODO instructions
1393 my $specialinstructions=" - convert to trusted mode\n";
1394
1395 # These are somewhat out-of-place, but only affect the text of the message.
1396 # Each of these messages is repeated in a separate TODO item in the
1397 # appropriate subroutine.
1398 if (&getGlobalConfig("AccountSecurity","single_user_password") eq "Y") {
1399 if (&GetDistro =~ "^HP-UX11.(.*)" and $1<23 ) {
1400 $specialinstructions .= " - set a single user password\n";
1401 }
1402 }
1403
1404 if (&getGlobalConfig("AccountSecurity","passwordpolicies") eq "Y") {
1405 $specialinstructions .= " - set trusted mode password policies\n";
1406 }
1407
1408 if (&getGlobalConfig("AccountSecurity", "PASSWORD_HISTORY_DEPTHyn") eq "Y") {
1409 $specialinstructions .= " - set a password history depth\n";
1410 }
1411
1412 if (&getGlobalConfig("AccountSecurity","system_auditing") eq "Y") {
1413 $specialinstructions .= " - enable auditing\n";
1414 }
1415
1416 my $saminstructions=
1417 "The security settings can be modified by running SAM as follows:\n" .
1418 "# sam\n" .
1419 "Next, go to the \"Auditing and Security Area\" and review\n" .
1420 "each sub-section. Make sure that you review all of your\n" .
1421 "settings, as some policies may seem restrictive.\n\n" .
1422 "On systems using the System Management Homepage, you can\n".
1423 "change your settings via the Tools:Security Attributes Configuration\n".
1424 "section. On some systems, you may also have the option of using SMH.\n\n";
1425
1426 # First, check for possible conflicts and corner cases
1427
1428 # check nsswitch for possible conflicts
1429 my $nsswitch = &getGlobal('FILE', 'nsswitch.conf');
1430 if ( -e $nsswitch) {
1431 open(FILE, $nsswitch);
1432 while (<FILE>) {
1433 if (/nis/ or /compat/ or /ldap/) {
1434 my $message = "Bastille found a possible conflict between trusted mode and\n" .
1435 "$nsswitch. Please remove all references to\n" .
1436 "\"compat\", \"nis\" and \"ldap\" in $nsswitch\n" .
1437 "and rerun Bastille, or use SAM/SMH to\n" .
1438 "$specialinstructions\n".
1439 "$saminstructions";
1440 close(FILE);
1441 return (0,$message);
1442 }
1443 }
1444 close(FILE);
1445 }
1446
1447 # check the namesvrs config file for possible NIS conflicts
1448 #Changed to unless "Y AND Y" since question can be skipped when nis is off
1449 # but corner cases can still exist, so check then too.
1450 unless ( &getGlobalConfig('MiscellaneousDaemons','nis_client') eq "Y" and
1451 &getGlobalConfig('MiscellaneousDaemons','nis_server') eq "Y" ) {
1452 my $namesvrs = &getGlobal('FILE', 'namesvrs');
1453 if (open(FILE, $namesvrs)) {
1454 while (<FILE>) {
1455 if (/^NIS.*=["]?1["]?$/) {
1456 my $message= "Possible conflict between trusted mode and NIS found.\n".
1457 "Please use SAM/SMH to\n" .
1458 " - turn off NIS\n" .
1459 "$specialinstructions\n".
1460 "$saminstructions";
1461 close(FILE);
1462 return (0,$message);
1463 }
1464 }
1465 close(FILE);
1466 } else {
1467 &B_log("ERROR","Unable to open $namesvrs for reading.");
1468 my $message= "Possible conflict between trusted mode and NIS found.\n".
1469 "Please use SAM/SMH to\n" .
1470 " - turn off NIS\n" .
1471 "$specialinstructions\n".
1472 "$saminstructions";
1473 return (0,$message);
1474 }
1475 if ( &B_match_line (&getGlobal("FILE","passwd"),"^\+:.*")) {
1476 my $message= '"+" entry found in passwd file. These are not\n' .
1477 "compatible with Trusted Mode. Either remove the entries\n" .
1478 "and re-run Bastille, or re-run Bastille, and direct it to\n" .
1479 "disable NIS client and server.\n";
1480 return (0,$message);
1481 }
1482
1483 }
1484
1485
1486 # check for conflicts with DCE integrated login
1487 my $authcmd = &getGlobal('BIN','auth.adm');
1488 if ( -e $authcmd ) {
1489 my $retval = system("PATH=/usr/bin $authcmd -q 1>/dev/null 2>&1");
1490 if ($retval != 0 and $retval != 1) {
1491 my $message="It appears that DCE integrated login is configured on this system.\n" .
1492 "DCE integrated login is incompatible with trusted systems and\n" .
1493 "auditing. Bastille is unable to\n" .
1494 "$specialinstructions" .
1495 "You will need to configure auditing and password policies using DCE.\n\n";
1496 return (0,$message);
1497 }
1498 }
1499
1500 if ( -e &getGlobal('FILE','shadow') ) {
1501 my $message="This system has already been converted to shadow passwords.\n" .
1502 "Shadow passwords are incompatible with trusted mode.\n" .
1503 "Bastille is unable to\n" .
1504 "$specialinstructions" .
1505 "If you desire these features, you should use\n".
1506 "\'pwunconv\' to change back to standard passwords,\n".
1507 "and then rerun Bastille.\n\n";
1508 return (0,$message);
1509 }
1510
1511 return (1,$saminstructions);
1512}
1513
1514# This routine allows Bastille to determine trusted-mode extension availability
1515
1516sub convertToShadow {
1517
1518 if (&isSystemTrusted) {
1519 # This is an internal error...Bastille should not call this routine
1520 # in this case. Error is here for robustness against future changes.
1521 &B_log("ERROR","This system is already converted to trusted mode.\n" .
1522 " Converting to shadow passwords will not be attempted.\n");
1523 return 0;
1524 }
1525
1526 # configuration files on which shadowed passwords depend
1527 my $nsswitch_conf = &getGlobal('FILE',"nsswitch.conf");
1528
1529 # binaries used to convert to a shadowed password
1530 my $pwconv = &getGlobal('BIN',"pwconv");
1531 my $echo = &getGlobal('BIN','echo'); # the echo is used to pipe a yes into the pwconv program as
1532 # pwconv requires user interaction.
1533
1534 # the binary used in a system revert.
1535 my $pwunconv = &getGlobal('BIN',"pwunconv");
1536 #check the password file for nis usage and if the nis client
1537 #or server is running.
1538 if(-e $nsswitch_conf) {
1539 # check the file for nis, nis+, compat, or dce usage.
1540 if(&B_match_line($nsswitch_conf, '^\s*passwd:.+(nis|nisplus|dce|compat)')) {
1541 my $shadowTODO = "\n---------------------------------\nHide encrypted passwords:\n" .
1542 "---------------------------------\n" .
1543 "This version of password shadowing does not support any repository other\n" .
1544 "than files. In order to convert your password database to shadowed passwords\n" .
1545 "there can be no mention of nis, nisplus, compat, or dce in the passwd\n" .
1546 "field of the \"$nsswitch_conf\" file. Please make the necessary edits to\n" .
1547 "the $nsswitch_conf file and run Bastille again using the command:\n" .
1548 "\"bastille -b\"\n";
1549 # Adding the shadowTODO comment to the TODO list.
1550 &B_TODO("$shadowTODO");
1551 # Notifing the user that the shadowed password coversion has failed.
1552 &B_log("ERROR","Password Shadowing Conversion Failed\n" .
1553 "$shadowTODO");
1554 # exiting the subroutine.
1555 return 0;
1556 }
1557
1558 }
1559
1560 # convert the password file to a shadowed repository.
1561 if (( -e $pwconv ) and ( -e $pwunconv ) and
1562 ( &B_System("$echo \"yes\" | $pwconv","$pwunconv") ) ){
1563 &B_TODO( "\n---------------------------------\nShadowing Password File:\n" .
1564 "---------------------------------\n" .
1565 "Your password file has been converted to use password shadowing.\n" .
1566 "This version of password shadowing does not support any repository other\n" .
1567 "than files. There can be no mention of nis, nisplus, compat, or dce\n" .
1568 "in the passwd field of the \"$nsswitch_conf\" file.\n\n" );
1569 } else {
1570 &B_log("ERROR","Conversion to shadow mode failed. The system may require ".
1571 "a patch to be capable of switching to shadow mode, or the ".
1572 "system my be in a state where conversion is not possible.");
1573 }
1574}
1575
1576
1577
1578##########################################################################
1579# &getSupportedSettings();
1580# Manipulates %trustedParameter and %isSupportedSetting, file-scoped variables
1581#
1582# Reads the password policy support matrix, which in-turn gives Bastille the
1583# places it should look for a given password policy setting.
1584
1585# Note the file was created like this so if could be maintained in an Excel(tm)
1586# spreadsheet, to optimize reviewability. TODO: consider other formats
1587
1588# File Format:
1589# HEADERS:<comment>,[<OS Version> <Mode> <Extensions>,]...
1590# [
1591# :<label>:<trusted equivalent>,,,,,,,,,,,,<comment>
1592# <action> (comment), [<test value>,]...
1593# ] ...
1594# Example;
1595# 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
1596#:ABORT_LOGIN_ON_MISSING_HOMEDIR,,,,,,,,,,,,,root
1597#/etc/security.dsc (search),x,,xx,x,x,x,!,!,!,!,!,!,
1598#/etc/default/security(search),y,y,y,y,y,y,y,y,y,y,y,y,
1599#getprdef (execute with <Trusted Equiv> argument),x,x,x,x,x,x,x,x,x,x,x,x,
1600
1601###########################################################################
1602our %trustedParameter = ();
1603our %isSupportedSetting = ();
1604
1605sub getSupportedSettings() {
1606
1607 my $line; # For a config file line
1608 my $linecount = 0;
1609 my $currentsetting = "";
1610 my @fields; # Fields in a given line
1611 my @columns; #Column Definitions
1612
1613
1614 &B_open(*SETTINGSFILE,&getGlobal('BFILE','AccountSecSupport'));
1615 my @settingLines=<SETTINGSFILE>;
1616 &B_close(*SETTINGSFILE);
1617
1618 #Remove blank-lines and comments
1619 @settingLines = grep(!/^#/,@settingLines);
1620 @settingLines = grep(!/^(\s*,+)*$/,@settingLines);
1621
1622 foreach $line (@settingLines) {
1623 ++$linecount;
1624 @fields = split(/,/,$line);
1625 if ($line =~ /^Information Source:/) { #Sets up colums
1626 my $fieldcount = 1; #Skipping first field
1627 while ((defined($fields[$fieldcount])) and
1628 ($fields[$fieldcount] =~ /\d+\.\d+/)){
1629 my @subfields = split(/ /,$fields[$fieldcount]);
1630 my $fieldsCount = @subfields;
1631 if ($fieldsCount != 3){
1632 &B_log("ERROR","Invalid subfield count: $fieldsCount in:".
1633 &getGlobal('BFILE','AccountSecSupport') .
1634 " line: $linecount and field: $fieldcount");
1635 }
1636 $columns[$fieldcount] = {OSVersion => $subfields[0],
1637 Mode => $subfields[1],
1638 Extension => $subfields[2] };
1639 &B_log("DEBUG","Found Header Column, $columns[$fieldcount]{'OSVersion'}, ".
1640 $columns[$fieldcount]{'Mode'} ." , " .
1641 $columns[$fieldcount]{'Extension'});
1642 ++$fieldcount;
1643 } # New Account Seting ex:
1644 } elsif ($line =~ /^:([^,:]+)(?::([^,]+))?/) { # :PASSWORD_WARNDAYS:expwarn,,,,,,,,,,,,
1645 $currentsetting = $1;
1646 if (defined($2)) {
1647 $trustedParameter{"$currentsetting"}=$2;
1648 }
1649 &B_log("DEBUG","Found Current Setting: ". $currentsetting .
1650 "/" . $trustedParameter{"$currentsetting"});
1651 } elsif (($line =~ /(^[^, :\)\(]+)[^,]*,((?:(?:[!y?nx]|!!),)+)/) and #normal line w/ in setting ex:
1652 ($currentsetting ne "")){ # security.dsc (search),x,x,x,x,x,!,!!,!,!,!,!,
1653 my $placeToLook = $1;
1654 my $fieldcount = 1; #Skip the first one, which we used in last line
1655 while (defined($fields[$fieldcount])) {
1656 &B_log("DEBUG","Setting $currentsetting : $columns[$fieldcount]{OSVersion} , ".
1657 "$columns[$fieldcount]{Mode} , ".
1658 "$columns[$fieldcount]{Extension} , ".
1659 "$placeToLook, to $fields[$fieldcount]");
1660 $isSupportedSetting{"$currentsetting"}
1661 {"$columns[$fieldcount]{OSVersion}"}
1662 {"$columns[$fieldcount]{Mode}"}
1663 {"$columns[$fieldcount]{Extension}"}
1664 {"$placeToLook"} =
1665 $fields[$fieldcount];
1666 ++$fieldcount;
1667 }
1668 } else {
1669 if ($line !~ /^,*/) {
1670 &B_log("ERROR","Incorrectly Formatted Line at ".
1671 &getGlobal('BFILE','AccountSecSupport') . ": $linecount");
1672 }
1673 }
1674 }
1675}
1676
1677##########################################################################
1678# &B_get_sec_value($param);
1679# This subroutine finds the value for a given user policy parameter.
1680# Specifically, it supports the parameters listed in the internal data structure
1681
1682# Return values:
1683# 'Not Defined' if the value is not present or not uniquely defined.
1684# $value if the value is present and unique
1685#
1686###########################################################################
1687sub B_get_sec_value($) {
1688 my $param=$_[0];
1689
1690 my $os_version;
1691 if (&GetDistro =~ /^HP-UX\D*(\d+\.\d+)/ ){
1692 $os_version=$1;
1693 } else {
1694 &B_log("ERROR","B_get_sec_value only supported on HP-UX");
1695 return undef;
1696 }
1697# my $sec_dsc = &getGlobal('FILE', 'security.dsc');
1698 my $sec_file = &getGlobal('FILE', 'security');
1699 my $getprdef = &getGlobal('BIN','getprdef');
1700 my $getprpw = &getGlobal('BIN','getprpw');
1701 my $userdbget = &getGlobal('BIN','userdbget');
1702 my $passwd = &getGlobal('BIN','passwd');
1703
1704 my $sec_flags = "";
1705 my @sec_settings=();
1706 my $user_sec_setting="";
1707
1708 my $security_mode="Standard";
1709 my $security_extension="no-SMSE";
1710
1711 &B_log("DEBUG","Entering get_sec_value for: $param");
1712
1713 sub isok ($) { # Locally-scoped subroutine, takes supported-matrix entry as argument
1714 my $supportedMatrixEntry = $_[0];
1715
1716 if ($supportedMatrixEntry =~ /!/) { #Matrix Entry for "Documented and/or tested"
1717 &B_log("DEBUG","isOk TRUE: $supportedMatrixEntry");
1718 return 1;
1719 } else {
1720 &B_log("DEBUG","isOk FALSE: $supportedMatrixEntry");
1721 return 0; #FALSE
1722 }
1723 } #end local subroutine
1724
1725 #Get Top Array item non-destructively
1726 sub getTop (@) {
1727 my @incomingArray = @_;
1728 my $topval = pop(@incomingArray);
1729 push(@incomingArray,$topval); #Probably redundant, but left in just in case.
1730 return $topval;
1731 }
1732
1733 sub ifExistsPushOnSecSettings($$) {
1734 my $sec_settings = $_[0];
1735 my $pushval = $_[1];
1736
1737 if ($pushval ne ""){
1738 push (@$sec_settings, $pushval);
1739 }
1740 }
1741
1742 #prpw and prdef both use "YES" instead of "1" like the other settings.
1743 sub normalizePolicy($){
1744 my $setting = $_[0];
1745
1746 $setting =~ s/YES/1/;
1747 $setting =~ s/NO/1/;
1748
1749 return $setting;
1750 }
1751
1752
1753
1754 if ((%trustedParameter == ()) or (%isSupportedSetting == ())) {
1755 # Manipulates %trustedParameter and %isSupportedSetting
1756 &getSupportedSettings;
1757 }
1758
1759 #First determine the security mode
1760 my $shadowFile = &getGlobal("FILE","shadow");
1761 my $passwdFile = &getGlobal("FILE","passwd");
1762
1763 if (&isSystemTrusted) {
1764 $security_mode = 'Trusted';
1765 } elsif ((-e $shadowFile) and #check file exist, and that passwd has no non-"locked" accounts
1766 (not(&B_match_line($passwdFile,'^[^\:]+:[^:]*[^:*x]')))) {
1767 $security_mode = 'Shadow';
1768 } else {
1769 $security_mode = 'Standard';
1770 }
1771 if (&isTrustedMigrationAvailable) {
1772 $security_extension = 'SMSE';
1773 } else {
1774 $security_extension = 'no-SMSE';
1775 }
1776 &B_log("DEBUG","Security mode: $security_mode extension: $security_extension");
1777 # Now look up the value from each applicable database, from highest precedence
1778 # to lowest:
1779 &B_log("DEBUG","Checking $param in userdbget");
1780 if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
1781 {$security_extension}{"userdbget_-a"})) {
1782 &ifExistsPushOnSecSettings(\@sec_settings,
1783 &B_getValueFromString('\w+\s+\w+=(\S+)',
1784 &B_Backtick("$userdbget -a $param")));
1785 &B_log("DEBUG", $param . ":userdbget setting: ". &getTop(@sec_settings));
1786 }
1787 &B_log("DEBUG","Checking $param in passwd");
1788 if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
1789 {$security_extension}{"passwd_-sa"})) {
1790 if ($param eq "PASSWORD_MINDAYS") {
1791 &ifExistsPushOnSecSettings(\@sec_settings,
1792 &B_getValueFromString('(?:\w+\s+){2}[\d\/]+\s+(\d+)\s+\d+',
1793 &B_Backtick("$passwd -s -a")));
1794 } elsif ($param eq "PASSWORD_MAXDAYS") {
1795 &ifExistsPushOnSecSettings(\@sec_settings,
1796 &B_getValueFromString('(?:\w+\s+){2}[\d\/]+\s+\d+\s+(\d+)',
1797 &B_Backtick("$passwd -s -a")));
1798 } elsif ($param eq "PASSWORD_WARNDAYS") {
1799 &ifExistsPushOnSecSettings(\@sec_settings,
1800 &B_getValueFromString('(?:\w+\s+){2}[\d\/]+(?:\s+\d+){2}\s+(\d+)',
1801 &B_Backtick("$passwd -s -a")));
1802 }
1803 &B_log("DEBUG", $param . ":passwd -sa setting: ". &getTop(@sec_settings));
1804 }
1805 &B_log("DEBUG","Checking $param in get prpw");
1806 if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
1807 {$security_extension}{"getprpw"})) {
1808 my $logins = &getGlobal("BIN","logins");
1809 my @userArray = split(/\n/,`$logins`);
1810 my $userParamVals = '';
1811 foreach my $rawuser (@userArray) {
1812 $rawuser =~ /^(\S+)/;
1813 my $user = $1;
1814 my $nextParamVal=&B_Backtick("$getprpw -l -m $trustedParameter{$param} $user");
1815 $nextParamVal =~ s/\w*=(-*[\w\d]*)/$1/;
1816 if ($nextParamVal != -1) { #Don't count users for which the local DB is undefined
1817 $userParamVals .= $user . "::::" . $nextParamVal ."\n";
1818 }
1819 } #Note getValueFromStrings deals with duplicates, returning "Not Unigue"
1820 my $policySetting = &B_getValueFromString('::::(\S+)',"$userParamVals");
1821 &ifExistsPushOnSecSettings (\@sec_settings, &normalizePolicy($policySetting));
1822 &B_log("DEBUG", $param . ":prpw setting: ". &getTop(@sec_settings));
1823 }
1824 &B_log("DEBUG","Checking $param in get prdef");
1825 if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
1826 {$security_extension}{"getprdef"})) {
1827 $_ = &B_Backtick ("$getprdef -m " . $trustedParameter{$param});
1828 /\S+=(\S+)/;
1829 my $policySetting = $1;
1830 &ifExistsPushOnSecSettings(\@sec_settings, &normalizePolicy($policySetting));
1831 &B_log("DEBUG", $param . ":prdef setting: ". &getTop(@sec_settings));
1832
1833 }
1834 &B_log("DEBUG","Checking $param in default security");
1835 if (&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
1836 {$security_extension}{"/etc/default/security"})) {
1837 &ifExistsPushOnSecSettings(\@sec_settings,&B_getValueFromFile('^\s*'. $param .
1838 '\s*=\s*([^\s#]+)\s*$', $sec_file));
1839 &B_log("DEBUG", $param . ":default setting: ". &getTop(@sec_settings));
1840 }
1841 #Commented below code in 3.0 release to avoid implication that bastille
1842 #had ever set these values explicitly, and the implications to runnable
1843 #config files where Bastille would then apply the defaults as actual policy
1844 #with possible conversion to shadow or similar side-effect.
1845
1846# &B_log("DEBUG","Checking $param in security.dsc");
1847 #security.dsc, only added in if valid for OS/mode/Extension, and nothing else
1848 #is defined (ie: @sec_settings=0)
1849# if ((&isok($isSupportedSetting{$param}{$os_version}{$security_mode}
1850# {$security_extension}{"/etc/security.dsc"})) and (@sec_settings == 0)) {
1851# &ifExistsPushOnSecSettings(\@sec_settings, &B_getValueFromFile('^' . $param .
1852# ';(?:[-\w/]*;){2}([-\w/]+);', $sec_dsc));
1853# &B_log("DEBUG", $param . ":security.dsc: ". &getTop(@sec_settings));
1854# }
1855
1856 # Return what we found
1857 my $last_setting=undef;
1858 my $current_setting=undef;
1859 while (@sec_settings > 0) {
1860 $current_setting = pop(@sec_settings);
1861 &B_log("DEBUG","Comparing $param configuration for identity: " .
1862 $current_setting);
1863 if ((defined($current_setting)) and ($current_setting ne '')) {
1864 if (not(defined($last_setting))){
1865 $last_setting=$current_setting;
1866 } elsif (($last_setting ne $current_setting) or
1867 ($current_setting eq 'Not Unique')){
1868 &B_log("DEBUG","$param setting not unique.");
1869 return 'Not Unique'; # Inconsistent state found, return 'Not Unique'
1870 }
1871 }
1872 }
1873 if ((not(defined($last_setting))) or ($last_setting eq '')) {
1874 return undef;
1875 } else {
1876 return $last_setting;
1877 }
1878
1879} #End B_get_sec_value
1880
1881sub secureIfNoNameService($){
1882 my $retval = $_[0];
1883
1884 if (&isUsingRemoteNameService) {
1885 return MANUAL();
1886 } else {
1887 return $retval;
1888 }
1889}
1890
1891#Specifically for cleartext protocols like NIS, which are not "secure"
1892sub isUsingRemoteNameService(){
1893
1894 if (&remoteServiceCheck('nis|nisplus|dce') == SECURE_CAN_CHANGE()){
1895 return 0; #false
1896 } else {
1897 return 1;
1898 }
1899}
1900
1901
1902
1903###########################################
1904## This is a wrapper for two functions that
1905## test the existence of nis-like configurations
1906## It is used by both the front end test and the back-end run
1907##############################################
1908sub remoteServiceCheck($){
1909 my $regex = $_[0];
1910
1911 my $nsswitch_conf = &getGlobal('FILE',"nsswitch.conf");
1912 my $passwd = &getGlobal('FILE',"passwd");
1913
1914 # check the file for nis usage.
1915 if (-e $nsswitch_conf) {
1916 if (&B_match_line($nsswitch_conf, '^\s*passwd:.*('. $regex . ')')) {
1917 return NOTSECURE_CAN_CHANGE();
1918 } elsif ((&B_match_line($nsswitch_conf, '^\s*passwd:.*(compat)')) and
1919 (&B_match_line($passwd, '^\s*\+'))) {
1920 return NOTSECURE_CAN_CHANGE(); # true
1921 }
1922 } elsif ((&B_match_line($passwd, '^\s*\+'))) {
1923 return NOTSECURE_CAN_CHANGE();
1924 }
1925
1926 my $oldnisdomain=&B_get_rc("NIS_DOMAIN");
1927 if ((($oldnisdomain eq "") or ($oldnisdomain eq '""')) and (&checkServiceOnHPUX('nis.client'))){
1928 return SECURE_CAN_CHANGE();
1929 }
1930 return NOTSECURE_CAN_CHANGE();
1931}
1932
1933#############################################
1934# remoteNISPlusServiceCheck
1935# test the existence of nis+ configuration
1936#############################################
1937sub remoteNISPlusServiceCheck () {
1938
1939 my $nsswitch_conf = &getGlobal('FILE',"nsswitch.conf");
1940
1941 # check the file for nis+ usage.
1942 if (-e $nsswitch_conf) {
1943 if (&B_match_line($nsswitch_conf, 'nisplus')) {
1944 return NOTSECURE_CAN_CHANGE();
1945 }
1946 }
1947
1948 return &checkServiceOnHPUX('nisp.client');
1949}
1950
1951
1952##########################################################################
1953# This subroutine creates nsswitch.conf file if the file not exists,
1954# and then append serveral services into the file if the service not
1955# exists in the file.
1956##########################################################################
1957sub B_create_nsswitch_file ($) {
1958 my $regex = $_[0];
1959
1960 my $nsswitch = &getGlobal('FILE',"nsswitch.conf");
1961
1962 if( ! -f $nsswitch ) {
1963 &B_create_file($nsswitch);
1964 # we don't need to revert the permissions change because we just
1965 # created the file
1966 chmod(0444, $nsswitch);
1967
1968 &B_append_line($nsswitch,'\s*passwd:', "passwd: $regex\n");
1969 &B_append_line($nsswitch,'\s*group:', "group: $regex\n");
1970 &B_append_line($nsswitch,'\s*hosts:', "hosts: $regex\n");
1971 &B_append_line($nsswitch,'\s*networks:', "networks: $regex\n");
1972 &B_append_line($nsswitch,'\s*protocols:', "protocols: $regex\n");
1973 &B_append_line($nsswitch,'\s*rpc:', "rpc: $regex\n");
1974 &B_append_line($nsswitch,'\s*publickey:', "publickey: $regex\n");
1975 &B_append_line($nsswitch,'\s*netgroup:', "netgroup: $regex\n");
1976 &B_append_line($nsswitch,'\s*automount:', "automount: $regex\n");
1977 &B_append_line($nsswitch,'\s*aliases:', "aliases: $regex\n");
1978 &B_append_line($nsswitch,'\s*services:', "services: $regex\n");
1979 }
1980}
1981
19821;
1983
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 @@
1package Bastille::API::Miscellaneous;
2use strict;
3
4use File::Path;
5use Bastille::API;
6use Bastille::API::HPSpecific;
7use Bastille::API::FileContent;
8
9require Exporter;
10our @ISA = qw(Exporter);
11our @EXPORT_OK = qw(
12PrepareToRun
13B_is_package_installed
14);
15our @EXPORT = @EXPORT_OK;
16
17
18###########################################################################
19#
20# PrepareToRun sets up Bastille to run. It checks the ARGV array for
21# special options and runs ConfigureForDistro to set necessary file
22# locations and other global variables.
23#
24###########################################################################
25
26sub PrepareToRun {
27
28 # Make sure we're root!
29 if ( $> != 0 ) {
30 &B_log("ERROR","Bastille must run as root!\n");
31 exit(1);
32 }
33
34
35 # Make any directories that don't exist...
36 foreach my $dir (keys %GLOBAL_BDIR) {
37 my $BdirPath = $GLOBAL_BDIR{$dir};
38 if ( $BdirPath =~ /^\s*\// ) { #Don't make relative directories
39 mkpath ($BdirPath,0,0700);
40 }
41 }
42
43 if(&GetDistro =~ "^HP-UX") {
44 &B_check_system;
45 }
46
47 &B_log("ACTION","\n########################################################\n" .
48 "# Begin Bastille Run #\n" .
49 "########################################################\n\n");
50
51 #read sum file if it exists.
52 &B_read_sums;
53
54
55# No longer necessary as flags are no longer in sum file, and sums are
56# are now checked "real time"
57
58 # check the integrity of the files listed
59# for my $file (sort keys %GLOBAL_SUM) {
60# &B_check_sum($file);
61# }
62 # write out the newly flagged sums
63# &B_write_sums;
64
65
66}
67
68
69
70###########################################################################
71# &B_is_package_installed($package);
72#
73# This function checks for the existence of the package named.
74#
75# TODO: Allow $package to be an expression.
76# TODO: Allow optional $version, $release, $epoch arguments so we can
77# make sure that the given package is at least as recent as some
78# given version number.
79#
80# scalar return values:
81# 0: $package is not installed
82# 1: $package is installed
83###########################################################################
84
85sub B_is_package_installed($) {
86 no strict;
87 my $package = $_[0];
88# Create a "global" variable with values scoped to this function
89# We do this to avoid having to repeatedly swlist/rpm
90# when we run B_is_package_installed
91local %INSTALLED_PACKAGE_LIST;
92
93 my $distro = &GetDistro;
94 if ($distro =~ /^HP-UX/) {
95 if (&checkProcsForService('swagent','ignore_warning') == SECURE_CANT_CHANGE()) {
96 &B_log("WARNING","Software Distributor Agent(swagent) is not running. Can not tell ".
97 "if package: $package is installed or not. Bastille will assume not. ".
98 "If the package is actually installed, Bastille may report or configure incorrectly.".
99 "To use Bastille-results as-is, please check to ensure $package is not installed, ".
100 "or re-run with the swagent running to get correct results.");
101 return 0; #FALSE
102 }
103 my $swlist=&getGlobal('BIN','swlist');
104 if (%INSTALLED_PACKAGE_LIST == () ) { # re-use prior results
105 if (open(SWLIST, "$swlist -a state -l fileset |")) {
106 while (my $line = <SWLIST>){
107 if ($line =~ /^ {2}\S+\.(\S+)\s*(\w+)/) {
108 $INSTALLED_PACKAGE_LIST{$1} = $2;
109 }
110 }
111 close SWLIST;
112 } else {
113 &B_log("ERROR","B_is_package_installed was unable to run the swlist command: $swlist,\n");
114 return FALSE;
115 }
116 }
117 # Now find the entry
118 if ($INSTALLED_PACKAGE_LIST{$package} == 'configured') {
119 return TRUE;
120 } else {
121 return FALSE;
122 }
123 } #End HP-UX Section
124 # This routine only works on RPM-based distros: Red Hat, Fedora, Mandrake and SuSE
125 elsif ( ($distro !~ /^RH/) and ($distro !~ /^MN/) and($distro !~ /^SE/) ) {
126 return 0;
127 } else { #This is a RPM-based distro
128 # Run an rpm command -- librpm is extremely messy, dynamic and not
129 # so much a perl thing. It's actually barely a C/C++ thing...
130 if (open RPM,"rpm -q $package") {
131 # We should get only one line back, but let's parse a few
132 # just in case.
133 my @lines = <RPM>;
134 close RPM;
135 #
136 # This is what we're trying to parse:
137 # $ rpm -q jay
138 # package jay is not installed
139 # $ rpm -q bash
140 # bash-2.05b-305.1
141 #
142
143 foreach $line (@lines) {
144 if ($line =~ /^package\s$package\sis\snot\sinstalled/) {
145 return 0;
146 }
147 elsif ($line =~ /^$package\-/) {
148 return 1;
149 }
150 }
151
152 # If we've read every line without finding one of these, then
153 # our parsing is broken
154 &B_log("ERROR","B_is_package_installed was unable to find a definitive RPM present or not present line.\n");
155 return 0;
156 } else {
157 &B_log("ERROR","B_is_package_installed was unable to run the RPM command,\n");
158 return 0;
159 }
160 }
161}
162
163
164
1651;
166
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 @@
1package Bastille::API::ServiceAdmin;
2use strict;
3
4use Bastille::API;
5
6use Bastille::API::HPSpecific;
7use Bastille::API::FileContent;
8
9require Exporter;
10our @ISA = qw(Exporter);
11our @EXPORT_OK = qw(
12B_chkconfig_on
13B_chkconfig_off
14B_service_start
15B_service_stop
16B_service_restart
17B_is_service_off
18checkServiceOnLinux
19remoteServiceCheck
20remoteNISPlusServiceCheck
21B_create_nsswitch_file
22);
23our @EXPORT = @EXPORT_OK;
24
25
26#######
27# &B_chkconfig_on and &B_chkconfig_off() are great for systems that didn't use
28# a more modern init system. This is a bit of a problem on Fedora, though,
29# which used upstart from Fedora 9 to Fedora 14, then switched to a new
30# Red Hat-created system called systemd for Fedora 15 and 16 (so far).
31# OpenSUSE also moved to systemd, starting with 12.1. Version 11.4 did not
32# use systemd.
33# It is also a problem on Ubuntu, starting at version 6.10, where they also
34# used upstart.
35#####
36
37
38
39
40###########################################################################
41# &B_chkconfig_on ($daemon_name) creates the symbolic links that are
42# named in the "# chkconfig: ___ _ _ " portion of the init.d files. We
43# need this utility, in place of the distro's chkconfig, because of both
44# our need to add revert functionality and our need to harden distros that
45# are not mounted on /.
46#
47# It uses the following global variables to find the links and the init
48# scripts, respectively:
49#
50# &getGlobal('DIR', "rcd") -- directory where the rc_.d subdirs can be found
51# &getGlobal('DIR', "initd") -- directory the rc_.d directories link to
52#
53# Here an example of where you might use this:
54#
55# You'd like to tell the system to run the firewall at boot:
56# B_chkconfig_on("bastille-firewall")
57#
58###########################################################################
59
60# PW: Blech. Copied B_chkconfig_off() and changed a few things,
61# then changed a few more things....
62
63sub B_chkconfig_on {
64
65 my $startup_script=$_[0];
66 my $retval=1;
67
68 my $chkconfig_line;
69 my ($runlevelinfo,@runlevels);
70 my ($start_order,$stop_order,$filetolink);
71
72 &B_log("ACTION","# chkconfig_on enabling $startup_script\n");
73
74 # In Debian system there is no chkconfig script, run levels are checked
75 # one by one (jfs)
76 if (&GetDistro =~/^DB.*/) {
77 $filetolink = &getGlobal('DIR', "initd") . "/$startup_script";
78 if (-x $filetolink)
79 {
80 foreach my $level ("0","1","2","3","4","5","6" ) {
81 my $link = '';
82 $link = &getGlobal('DIR', "rcd") . "/rc" . "$level" . ".d/K50" . "$startup_script";
83 $retval=symlink($filetolink,$link);
84 }
85 }
86 return $retval;
87 }
88 #
89 # On SUSE, chkconfig-based rc scripts have been replaced with a whole different
90 # system. chkconfig on SUSE is actually a shell script that does some stuff and then
91 # calls insserv, their replacement.
92 #
93
94 if (&GetDistro =~ /^SE/) {
95 # only try to chkconfig on if init script is found
96 if ( -e (&getGlobal('DIR', "initd") . "/$startup_script") ) {
97 $chkconfig_line=&getGlobal('BIN','chkconfig');
98 &B_System("$chkconfig_line $startup_script on", "$chkconfig_line $startup_script off");
99 # chkconfig doesn't take affect until reboot, need to restart service also
100 B_service_restart("$startup_script");
101 return 1; #success
102 }
103 return 0; #failure
104 }
105
106 #
107 # Run through the init script looking for the chkconfig line...
108 #
109 $retval = open CHKCONFIG,&getGlobal('DIR', "initd") . "/$startup_script";
110 unless ($retval) {
111 &B_log("ACTION","# Didn't chkconfig_on $startup_script because we couldn't open " . &getGlobal('DIR', "initd") . "/$startup_script\n");
112 }
113 else {
114
115 READ_LOOP:
116 while (my $line=<CHKCONFIG>) {
117
118 # We're looking for lines like this one:
119 # # chkconfig: 2345 10 90
120 # OR this
121 # # chkconfig: - 10 90
122
123 if ($line =~ /^#\s*chkconfig:\s*([-\d]+)\s*(\d+)\s*(\d+)/ ) {
124 $runlevelinfo = $1;
125 $start_order = $2;
126 $stop_order = $3;
127 # handle a run levels arg of '-'
128 if ( $runlevelinfo eq '-' ) {
129 &B_log("ACTION","chkconfig_on saw '-' for run levels for \"$startup_script\", is defaulting to levels 3,4,5\n");
130 $runlevelinfo = '345';
131 }
132 @runlevels = split(//,$runlevelinfo);
133 # make sure the orders have 2 digits
134 $start_order =~ s/^(\d)$/0$1/;
135 $stop_order =~ s/^(\d)$/0$1/;
136 last READ_LOOP;
137 }
138 }
139 close CHKCONFIG;
140
141 # Do we have what we need?
142 if ( (scalar(@runlevels) < 1) || (! $start_order =~ /^\d{2}$/) || (! $stop_order =~ /^\d{2}$/) ) {
143 # problem
144 &B_log("ERROR","# B_chkconfig_on $startup_script failed -- no valid run level/start/stop info found\n");
145 return(-1);
146 }
147
148 # Now, run through creating symlinks...
149 &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");
150
151 $retval=0;
152 # BUG: we really ought to readdir() on &getGlobal('DIR', "rcd") to get all levels
153 foreach my $level ( "0","1","2","3","4","5","6" ) {
154 my $link = '';
155 # we make K links in run levels not specified in the chkconfig line
156 $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/K$stop_order" . $startup_script;
157 my $klink = $link;
158 # now we see if this is a specified run level; if so, make an S link
159 foreach my $markedlevel ( @runlevels ) {
160 if ( $level == $markedlevel) {
161 $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/S$start_order" . $startup_script;
162 }
163 }
164 my $target = &getGlobal('DIR', "initd") ."/" . $startup_script;
165 my $local_return;
166
167 if ( (-e "$klink") && ($klink ne $link) ) {
168 # there's a K link, but this level needs an S link
169 unless ($GLOBAL_LOGONLY) {
170 $local_return = unlink("$klink");
171 if ( ! $local_return ) {
172 # unlinking old, bad $klink failed
173 &B_log("ERROR","Unlinking $klink failed\n");
174 } else {
175 &B_log("ACTION","Removed link $klink\n");
176 # If we removed the link, add a link command to the revert file
177 &B_revert_log (&getGlobal('BIN','ln') . " -s $target $klink\n");
178 } # close what to do if unlink works
179 } # if not GLOBAL_LOGONLY
180 } # if $klink exists and ne $link
181
182 # OK, we've disposed of any old K links, make what we need
183 if ( (! ( -e "$link" )) && ($link ne '') ) {
184 # link doesn't exist and the start/stop number is OK; make it
185 unless ($GLOBAL_LOGONLY) {
186 # create the link
187 $local_return = &B_symlink($target,$link);
188 if ($local_return) {
189 $retval++;
190 &B_log("ACTION","Created link $link\n");
191 } else {
192 &B_log("ERROR","Couldn't create $link when trying to chkconfig on $startup_script\n");
193 }
194 }
195
196 } # link doesn't exist
197 } # foreach level
198
199 }
200
201 if ($retval < @runlevels) {
202 $retval=0;
203 }
204
205 $retval;
206
207}
208
209
210###########################################################################
211# &B_chkconfig_off ($daemon_name) deletes the symbolic links that are
212# named in the "# chkconfig: ___ _ _ " portion of the init.d files. We
213# need this utility, in place of the distro's chkconfig, because of both
214# our need to add revert functionality and our need to harden distros that
215# are not mounted on /.
216#
217# chkconfig allows for a REVERT of its work by writing to an executable
218# file &getGlobal('BFILE', "removed-symlinks").
219#
220# It uses the following global variables to find the links and the init
221# scripts, respectively:
222#
223# &getGlobal('DIR', "rcd") -- directory where the rc_.d subdirs can be found
224# &getGlobal('DIR', "initd") -- directory the rc_.d directories link to
225#
226# Here an example of where you might use this:
227#
228# You'd like to tell stop running sendmail in daemon mode on boot:
229# B_chkconfig_off("sendmail")
230#
231###########################################################################
232
233
234
235sub B_chkconfig_off {
236
237 my $startup_script=$_[0];
238 my $retval=1;
239
240 my $chkconfig_line;
241 my @runlevels;
242 my ($start_order,$stop_order,$filetolink);
243
244 if (&GetDistro =~/^DB.*/) {
245 $filetolink = &getGlobal('DIR', "initd") . "/$startup_script";
246 if (-x $filetolink)
247 {
248 # Three ways to do this in Debian:
249 # 1.- have the initd script set to 600 mode
250 # 2.- Remove the links in rcd (re-installing the package
251 # will break it)
252 # 3.- Use update-rc.d --remove (same as 2.)
253 # (jfs)
254 &B_chmod(0600,$filetolink);
255 $retval=6;
256
257 # The second option
258 #foreach my $level ("0","1","2","3","4","5","6" ) {
259 #my $link = '';
260 #$link = &getGlobal('DIR', "rcd") . "/rc" . "$level" . ".d/K50" . "$startup_script";
261 #unlink($link);
262 #}
263 }
264 }
265
266 #
267 # On SUSE, chkconfig-based rc scripts have been replaced with a whole different
268 # system. chkconfig on SUSE is actually a shell script that does some stuff and then
269 # calls insserv, their replacement.
270 #
271 elsif (&GetDistro =~ /^SE/) {
272 # only try to chkconfig off if init script is found
273 if ( -e (&getGlobal('DIR', "initd") . "/$startup_script") ) {
274 $chkconfig_line=&getGlobal('BIN','chkconfig');
275 &B_System("$chkconfig_line $startup_script on", "$chkconfig_line $startup_script off");
276 # chkconfig doesn't take affect until reboot, need to stop service
277 # since expectation is that the daemons are disabled even without a reboot
278 B_service_stop("$startup_script");
279 return 1; #success
280 }
281 return 0; #failure
282 }
283 else {
284
285 # Run through the init script looking for the chkconfig line...
286
287
288 $retval = open CHKCONFIG,&getGlobal('DIR', "initd") . "/$startup_script";
289 unless ($retval) {
290 &B_log("ACTION","Didn't chkconfig_off $startup_script because we couldn't open " . &getGlobal('DIR', "initd") . "/$startup_script\n");
291 }
292 else {
293
294 READ_LOOP:
295 while (my $line=<CHKCONFIG>) {
296
297 # We're looking for lines like this one:
298 # # chkconfig: 2345 10 90
299
300 if ($line =~ /^#\s*chkconfig:\s*([-\d]+)\s*(\d+)\s*(\d+)/ ) {
301 @runlevels=split //,$1;
302 $start_order=$2;
303 $stop_order=$3;
304
305
306 # Change single digit run levels to double digit -- otherwise,
307 # the alphabetic ordering chkconfig depends on fails.
308 if ($start_order =~ /^\d$/ ) {
309 $start_order = "0" . $start_order;
310 &B_log("ACTION","chkconfig_off converted start order to $start_order\n");
311 }
312 if ($stop_order =~ /^\d$/ ) {
313 $stop_order = "0" . $stop_order;
314 &B_log("ACTION","chkconfig_off converted stop order to $stop_order\n");
315 }
316
317 last READ_LOOP;
318 }
319 }
320 close CHKCONFIG;
321
322 # If we never found a chkconfig line, can we just run through all 5
323 # rcX.d dirs from 1 to 5...?
324
325 # unless ( $start_order and $stop_order ) {
326 # @runlevels=("1","2","3","4","5");
327 # $start_order = "*"; $stop_order="*";
328 # }
329
330 # Now, run through removing symlinks...
331
332
333
334 $retval=0;
335
336 # Handle the special case that the run level specified is solely "-"
337 if ($runlevels[0] =~ /-/) {
338 @runlevels = ( "0","1","2","3","4","5","6" );
339 }
340
341 foreach my $level ( @runlevels ) {
342 my $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/S$start_order" . $startup_script;
343 my $new_link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/K$stop_order" . $startup_script;
344 my $target = &getGlobal('DIR', "initd") ."/" . $startup_script;
345 my $local_return;
346
347
348 # Replace the S__ link in this level with a K__ link.
349 if ( -e $link ) {
350 unless ($GLOBAL_LOGONLY) {
351 $local_return=unlink $link;
352 if ($local_return) {
353 $local_return=symlink $target,$new_link;
354 unless ($local_return) {
355 &B_log("ERROR","Linking $target to $new_link failed.\n");
356 }
357 }
358 else { # unlinking failed
359 &B_log("ERROR","Unlinking $link failed\n");
360 }
361
362 }
363 if ($local_return) {
364 $retval++;
365 &B_log("ACTION","Removed link $link\n");
366
367 #
368 # If we removed the link, add a link command to the revert file
369 # Write out the revert information for recreating the S__
370 # symlink and deleting the K__ symlink.
371 &B_revert_log(&getGlobal('BIN',"ln") . " -s $target $link\n");
372 &B_revert_log(&getGlobal('BIN',"rm") . " -f $new_link\n");
373 }
374 else {
375 &B_log("ERROR","B_chkconfig_off $startup_script failed\n");
376 }
377
378 }
379 } # foreach
380
381 } # else-unless
382
383 } # else-DB
384 if ($retval < @runlevels) {
385 $retval=0;
386 }
387
388 $retval;
389
390}
391
392
393###########################################################################
394# &B_service_start ($daemon_name)
395# Starts service on RedHat/SUSE-based Linux distributions which have the
396# service command:
397#
398# service $daemon_name start
399#
400# Other Linux distros that also support this method of starting
401# services can be added to use this function.
402#
403# Here an example of where you might use this:
404#
405# You'd like to tell the system to start the vsftpd daemon:
406# &B_service_start("vsftpd")
407#
408# Uses &B_System in HP_API.pm
409# To match how the &B_System command works this method:
410# returns 1 on success
411# returns 0 on failure
412###########################################################################
413
414sub B_service_start {
415
416 my $daemon=$_[0];
417
418 if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
419 (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
420 &B_log("ERROR","Tried to call service_start on a system lacking a service command! Internal Bastille error.");
421 return undef;
422 }
423
424 # only start service if init script is found
425 if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
426 &B_log("ACTION","# service_start enabling $daemon\n");
427
428 my $service_cmd=&getGlobal('BIN', 'service');
429 if ($service_cmd) {
430 # Start the service,
431 # Also provide &B_System revert command
432
433 return (&B_System("$service_cmd $daemon start",
434 "$service_cmd $daemon stop"));
435 }
436 }
437
438 # init script not found, do not try to start, return failure
439 return 0;
440}
441
442###########################################################################
443# &B_service_stop ($daemon_name)
444# Stops service on RedHat/SUSE-based Linux distributions which have the
445# service command:
446#
447# service $daemon_name stop
448#
449# Other Linux distros that also support this method of starting
450# services can be added to use this function.
451# Stops service.
452#
453#
454# Here an example of where you might use this:
455#
456# You'd like to tell the system to stop the vsftpd daemon:
457# &B_service_stop("vsftpd")
458#
459# Uses &B_System in HP_API.pm
460# To match how the &B_System command works this method:
461# returns 1 on success
462# returns 0 on failure
463###########################################################################
464
465sub B_service_stop {
466
467 my $daemon=$_[0];
468
469 if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
470 (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
471 &B_log("ERROR","Tried to call service_stop on a system lacking a service command! Internal Bastille error.");
472 return undef;
473 }
474
475 # only stop service if init script is found
476 if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
477 &B_log("ACTION","# service_stop disabling $daemon\n");
478
479 my $service_cmd=&getGlobal('BIN', 'service');
480 if ($service_cmd) {
481
482 # Stop the service,
483 # Also provide &B_System revert command
484
485 return (&B_System("$service_cmd $daemon stop",
486 "$service_cmd $daemon start"));
487 }
488 }
489
490 # init script not found, do not try to stop, return failure
491 return 0;
492}
493
494
495###########################################################################
496# &B_service_restart ($daemon_name)
497# Restarts service on RedHat/SUSE-based Linux distributions which have the
498# service command:
499#
500# service $daemon_name restart
501#
502# Other Linux distros that also support this method of starting
503# services can be added to use this function.
504#
505# Here an example of where you might use this:
506#
507# You'd like to tell the system to restart the vsftpd daemon:
508# &B_service_restart("vsftpd")
509#
510# Uses &B_System in HP_API.pm
511# To match how the &B_System command works this method:
512# returns 1 on success
513# returns 0 on failure
514###########################################################################
515
516sub B_service_restart {
517
518 my $daemon=$_[0];
519
520 if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
521 (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
522 &B_log("ERROR","Tried to call service_restart on a system lacking a service command! Internal Bastille error.");
523 return undef;
524 }
525
526 # only restart service if init script is found
527 if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
528 &B_log("ACTION","# service_restart re-enabling $daemon\n");
529
530 my $service_cmd=&getGlobal('BIN', 'service');
531 if ($service_cmd) {
532
533 # Restart the service
534 return (&B_System("$service_cmd $daemon restart",
535 "$service_cmd $daemon restart"));
536 }
537 }
538
539 # init script not found, do not try to restart, return failure
540 return 0;
541}
542
543###########################################################################
544# &B_is_service_off($;$)
545#
546# Runs the specified test to determine whether or not the question should
547# be answered.
548#
549# return values:
550# NOTSECURE_CAN_CHANGE()/0: service is on
551# SECURE_CANT_CHANGE()/1: service is off
552# undef: test is not defined
553###########################################################################
554
555sub B_is_service_off ($){
556 my $service=$_[0];
557
558 if(&GetDistro =~ "^HP-UX"){
559 #die "Why do I think I'm on HPUX?!\n";
560 return &checkServiceOnHPUX($service);
561 }
562 elsif ( (&GetDistro =~ "^RH") || (&GetDistro =~ "^SE") ) {
563 return &checkServiceOnLinux($service);
564 }
565 else {
566 &B_log("DEBUG","B_is_service off called for unsupported OS");
567 # not yet implemented for other distributions of Linux
568 # when GLOBAL_SERVICE, GLOBAL_SERVTYPE and GLOBAL_PROCESS are filled
569 # in for Linux, then
570 # at least inetd and inittab services should be similar to the above,
571 # whereas chkconfig would be used on some Linux distros to determine
572 # if non-inetd/inittab services are running at boot time. Looking at
573 # processes should be similar.
574 return undef;
575 }
576}
577
578###########################################################################
579# &checkServiceOnLinux($service);
580#
581# Checks if the given service is running on a Linux system. This is
582# called by B_is_Service_Off(), which is the function that Bastille
583# modules should call.
584#
585# Return values:
586# NOTSECURE_CAN_CHANGE() if the service is on
587# SECURE_CANT_CHANGE() if the service is off
588# undef if the state of the service cannot be determined
589#
590###########################################################################
591sub checkServiceOnLinux($) {
592 my $service=$_[0];
593
594 # get the list of parameters which could be used to initiate the service
595 # (could be in /etc/rc.d/rc?.d, /etc/inetd.conf, or /etc/inittab, so we
596 # check all of them)
597
598 my @params = @{ &getGlobal('SERVICE', $service) };
599 my $chkconfig = &getGlobal('BIN', 'chkconfig');
600 my $grep = &getGlobal('BIN', 'grep');
601 my $inittab = &getGlobal('FILE', 'inittab');
602 my $serviceType = &getGlobal('SERVTYPE', $service);;
603
604 # A kludge to get things running because &getGlobal('SERVICE' doesn't
605 # return the expected values.
606 @params = ();
607 push (@params, $service);
608
609 foreach my $param (@params) {
610 &B_log("DEBUG","Checking to see if service $service is off.\n");
611
612 if ($serviceType =~ /rc/) {
613 my $on = &B_Backtick("$chkconfig --list $param 2>&1");
614 if ($on =~ /^$param:\s+unknown/) {
615 # This service isn't installed on the system
616 return NOT_INSTALLED();
617 }
618 if ($on =~ /^error reading information on service $param: No such file or directory/) {
619 # This service isn't installed on the system
620 return NOT_INSTALLED();
621 }
622 if ($on =~ /^error/) {
623 # This probably
624 &B_log("DEBUG","chkconfig returned: $param=$on\n");
625 return undef;
626 }
627 $on =~ s/^$param\s+//; # remove the service name and spaces
628 $on =~ s/[0-6]:off\s*//g; # remove any runlevel:off entries
629 $on =~ s/:on\s*//g; # remove the :on from the runlevels
630 # what remains is a list of runlevels in which the service is on,
631 # or a null string if it is never turned on
632 chomp $on; # newline should be gone already (\s)
633 &B_log("DEBUG","chkconfig returned: $param=$on\n");
634
635 if ($on =~ /^\d+$/) {
636 # service is not off
637 ########################### BREAK out, don't skip question
638 return NOTSECURE_CAN_CHANGE();
639 }
640 }
641 elsif ($serviceType =~ /inet/) {
642 my $on = &B_Backtick("$chkconfig --list $param 2>&1");
643 if ($on =~ /^$param:\s+unknown/) {
644 # This service isn't installed on the system
645 return NOT_INSTALLED();
646 }
647 if ($on =~ /^error reading information on service $param: No such file or directory/) {
648 # This service isn't installed on the system
649 return NOT_INSTALLED();
650 }
651 if ($on =~ /^error/ ) {
652 # Something else is wrong?
653 # return undef
654 return undef;
655 }
656 if ($on =~ tr/\n// > 1) {
657 $on =~ s/^xinetd.+\n//;
658 }
659 $on =~ s/^\s*$param:?\s+//; # remove the service name and spaces
660 chomp $on; # newline should be gone already (\s)
661 &B_log("DEBUG","chkconfig returned: $param=$on\n");
662
663 if ($on =~ /^on$/) {
664 # service is not off
665 ########################### BREAK out, don't skip question
666 return NOTSECURE_CAN_CHANGE();
667 }
668 }
669 else {
670 # perhaps the service is started by inittab
671 my $inittabline = &B_Backtick("$grep -E '^[^#].{0,3}:.*:.+:.*$param' $inittab");
672 if ($inittabline =~ /.+/) { # . matches anything except newlines
673 # service is not off
674 &B_log("DEBUG","Checking inittab; found $inittabline\n");
675 ########################### BREAK out, don't skip question
676 return NOTSECURE_CAN_CHANGE();
677 }
678 }
679 } # foreach my $param
680
681
682 # boot-time parameters are not set; check processes
683 # Note the checkProcsforService returns INCONSISTENT() if a process is found
684 # assuming the checks above
685 return &checkProcsForService($service);
686}
687
6881;
689
690
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 @@
1# Q: Would you like to enforce password aging? [Y]
2AccountSecurity.passwdage="Y"
3# Q: Should Bastille disable clear-text r-protocols that use IP-based authentication? [Y]
4AccountSecurity.protectrhost="Y"
5# Q: Should we disallow root login on tty's 1-6? [N]
6AccountSecurity.rootttylogins="Y"
7# Q: What umask would you like to set for users on the system? [077]
8AccountSecurity.umask="077"
9# Q: Do you want to set the default umask? [Y]
10AccountSecurity.umaskyn="Y"
11# Q: Would you like to deactivate the Apache web server? [Y]
12Apache.apacheoff="Y"
13# Q: Would you like to password protect single-user mode? [Y]
14BootSecurity.passsum="Y"
15# Q: Should we restrict console access to a small group of user accounts? [N]
16ConfigureMiscPAM.consolelogin="Y"
17# Q: Which accounts should be able to login at console? [root]
18ConfigureMiscPAM.consolelogin_accounts="root"
19# Q: Would you like to put limits on system resource usage? [N]
20ConfigureMiscPAM.limitsconf="Y"
21# Q: Would you like to set more restrictive permissions on the administration utilities? [N]
22FilePermissions.generalperms_1_1="Y"
23# Q: Would you like to disable SUID status for mount/umount?
24FilePermissions.suidmount="Y"
25# Q: Would you like to disable SUID status for ping? [Y]
26FilePermissions.suidping="Y"
27# Q: Would you like to disable SUID status for traceroute? [Y]
28FilePermissions.suidtrace="Y"
29# Q: Do you need the advanced networking options?
30Firewall.ip_advnetwork="Y"
31# Q: Should Bastille run the firewall and enable it at boot time? [N]
32Firewall.ip_enable_firewall="Y"
33# Q: Would you like to run the packet filtering script? [N]
34Firewall.ip_intro="Y"
35# Q: Interfaces for DHCP queries: [ ]
36Firewall.ip_s_dhcpiface=" "
37# Q: DNS servers: [0.0.0.0/0]
38Firewall.ip_s_dns="10.184.9.1"
39# Q: ICMP allowed types: [destination-unreachable echo-reply time-exceeded]
40Firewall.ip_s_icmpallowed="destination-unreachable echo-reply time-exceeded"
41# Q: ICMP services to audit: [ ]
42Firewall.ip_s_icmpaudit=" "
43# Q: ICMP types to disallow outbound: [destination-unreachable time-exceeded]
44Firewall.ip_s_icmpout="destination-unreachable time-exceeded"
45# Q: Internal interfaces: [ ]
46Firewall.ip_s_internaliface=" "
47# Q: TCP service names or port numbers to allow on private interfaces: [ ]
48Firewall.ip_s_internaltcp=" "
49# Q: UDP service names or port numbers to allow on private interfaces: [ ]
50Firewall.ip_s_internaludp=" "
51# Q: Masqueraded networks: [ ]
52Firewall.ip_s_ipmasq=" "
53# Q: Kernel modules to masquerade: [ftp raudio vdolive]
54Firewall.ip_s_kernelmasq="ftp raudio vdolive"
55# Q: NTP servers to query: [ ]
56Firewall.ip_s_ntpsrv=" "
57# Q: Force passive mode? [N]
58Firewall.ip_s_passiveftp="N"
59# Q: Public interfaces: [eth+ ppp+ slip+]
60Firewall.ip_s_publiciface="eth+ ppp+ slip+"
61# Q: TCP service names or port numbers to allow on public interfaces:[ ]
62Firewall.ip_s_publictcp=" "
63# Q: UDP service names or port numbers to allow on public interfaces:[ ]
64Firewall.ip_s_publicudp=" "
65# Q: Reject method: [DENY]
66Firewall.ip_s_rejectmethod="DENY"
67# Q: Enable source address verification? [Y]
68Firewall.ip_s_srcaddr="Y"
69# Q: TCP services to audit: [telnet ftp imap pop3 finger sunrpc exec login linuxconf ssh]
70Firewall.ip_s_tcpaudit="telnet ftp imap pop3 finger sunrpc exec login linuxconf ssh"
71# Q: TCP services to block: [2049 2065:2090 6000:6020 7100]
72Firewall.ip_s_tcpblock="2049 2065:2090 6000:6020 7100"
73# Q: Trusted interface names: [lo]
74Firewall.ip_s_trustiface="lo"
75# Q: UDP services to audit: [31337]
76Firewall.ip_s_udpaudit="31337"
77# Q: UDP services to block: [2049 6770]
78Firewall.ip_s_udpblock="2049 6770"
79# Q: Would you like to add additional logging? [Y]
80Logging.morelogging="Y"
81# Q: Would you like to set up process accounting? [N]
82Logging.pacct="N"
83# Q: Do you have a remote logging host? [N]
84Logging.remotelog="N"
85# Q: Would you like to disable acpid and/or apmd? [Y]
86MiscellaneousDaemons.apmd="Y"
87# Q: Would you like to deactivate NFS and Samba? [Y]
88MiscellaneousDaemons.remotefs="Y"
89# Q: Would you like to disable printing? [N]
90Printing.printing="Y"
91# Q: Would you like to disable printing? [N]
92Printing.printing_cups="Y"
93# Q: Would you like to display "Authorized Use" messages at log-in time? [Y]
94SecureInetd.banners="Y"
95# Q: Should Bastille ensure inetd's FTP service does not run on this system? [y]
96SecureInetd.deactivate_ftp="Y"
97# Q: Should Bastille ensure the telnet service does not run on this system? [y]
98SecureInetd.deactivate_telnet="Y"
99# Q: Who is responsible for granting authorization to use this machine?
100SecureInetd.owner="its owner"
101# Q: Would you like to set a default-deny on TCP Wrappers and xinetd? [N]
102SecureInetd.tcpd_default_deny="Y"
103# Q: Do you want to stop sendmail from running in daemon mode? [Y]
104Sendmail.sendmaildaemon="Y"
105# Q: Would you like to install TMPDIR/TMP scripts? [N]
106TMPDIR.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 @@
1Index: Bastille/bin/bastille
2===================================================================
3--- Bastille.orig/bin/bastille
4+++ Bastille/bin/bastille
5@@ -162,11 +162,12 @@ fi
6 # We check that the version is at least the minimum
7
8 PERL_VERSION=`${CURRENT_PERL_PATH}/perl -version |
9- head -2 | # the second line contains the version
10+ head -n 2 | # the second line contains the version
11 tr " " "\n" | # split words into separate lines
12- sed -e "s/^v//" | # to get rid of the v in v5.6.0
13- grep "^[1-9]\." | # find a "word" that starts with number dot
14- sed -e "s/_/./"` # substitute _patchlevel with .patchlevel
15+ grep "^(v" | # find a "word" that starts with '(v'
16+ sed -e "s/^(v//" -e "s/)//" -e "s/_/./"`
17+ # to get rid of the (v in v5.6.0
18+ # substitute _patchlevel with .patchlevel
19 # (used in 5.005_03 and prior)
20
21 # 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 @@
1From c59b84ca3bda8e4244d47901b6966f28dd675434 Mon Sep 17 00:00:00 2001
2From: Andrei Dinu <andrei.adrianx.dinu@intel.com>
3Date: Thu, 23 May 2013 15:12:23 +0300
4Subject: [PATCH] added yocto-standard to bastille
5
6In order to make Bastille functional and avoid errors
7regarding distros, if not any given distro is identified,
8yocto-standard distro is added to the distro variable
9in Bastille.
10
11Fixed also some warnings regarding defined statements
12in API.pm.
13
14Signed-off-by: Andrei Dinu <andrei.adrianx.dinu@intel.com>
15---
16 Bastille/API.pm | 12 ++++++------
17 1 file changed, 6 insertions(+), 6 deletions(-)
18
19diff --git a/Bastille/API.pm b/Bastille/API.pm
20index 40f8c72..ebbe9f7 100644
21--- a/Bastille/API.pm
22+++ b/Bastille/API.pm
23@@ -445,8 +445,8 @@ sub GetDistro() {
24 $release=`/usr/bin/uname -sr`;
25 }
26 else {
27- print STDERR "$err Could not determine operating system version!\n";
28- $distro="unknown";
29+ #print STDERR "$err Could not determine operating system version!\n";
30+ $distro="3.8.11-yocto-standard";
31 }
32
33 # Figure out what kind of system we're on.
34@@ -537,7 +537,7 @@ sub getSupportedOSHash () {
35 "DB2.2", "DB3.0",
36 "RH6.0","RH6.1","RH6.2","RH7.0",
37 "RH7.1","RH7.2","RH7.3","RH8.0",
38- "RH9",
39+ "RH9","3.8.11-yocto-standard",
40 "RHEL5",
41 "RHEL4AS","RHEL4ES","RHEL4WS",
42 "RHEL3AS","RHEL3ES","RHEL3WS",
43@@ -1284,7 +1284,7 @@ sub B_write_sums {
44
45 my $sumFile = &getGlobal('BFILE',"sum.csv");
46
47- if ( defined %GLOBAL_SUM ) {
48+ if ( %GLOBAL_SUM ) {
49
50 open( SUM, "> $sumFile") or &B_log("ERROR","Unable to open $sumFile for write.\n$!\n");
51
52@@ -1318,7 +1318,7 @@ sub B_check_sum($) {
53 my $file = $_[0];
54 my $cksum = &getGlobal('BIN',"cksum");
55
56- if (not(defined(%GLOBAL_SUM))) {
57+ if (not(%GLOBAL_SUM)) {
58 &B_read_sums;
59 }
60
61@@ -1375,7 +1375,7 @@ sub listModifiedFiles {
62 sub B_isFileinSumDB($) {
63 my $file = $_[0];
64
65- if (not(defined(%GLOBAL_SUM))) {
66+ if (not(%GLOBAL_SUM)) {
67 &B_log("DEBUG","Reading in DB from B_isFileinSumDB");
68 &B_read_sums;
69 }
70--
711.7.9.5
72