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 2529