blob: 5060f52a4ebba50832d13c328b98c0b63517e06c [file] [log] [blame]
Richard Marian Thomaiyar14fddef2018-07-13 23:55:56 +05301# 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