diff options
Diffstat (limited to 'meta-security/recipes-security/bastille/files/FileContent.pm')
-rw-r--r-- | meta-security/recipes-security/bastille/files/FileContent.pm | 1153 |
1 files changed, 1153 insertions, 0 deletions
diff --git a/meta-security/recipes-security/bastille/files/FileContent.pm b/meta-security/recipes-security/bastille/files/FileContent.pm new file mode 100644 index 000000000..0a5d6096c --- /dev/null +++ b/meta-security/recipes-security/bastille/files/FileContent.pm @@ -0,0 +1,1153 @@ +package Bastille::API::FileContent; +use strict; + +use Bastille::API; + +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw( +B_blank_file +B_insert_line_after +B_insert_line_before +B_insert_line +B_append_line +B_prepend_line +B_replace_line +B_replace_lines +B_replace_pattern +B_match_line +B_match_line_only +B_match_chunk +B_return_matched_lines +B_hash_comment_line +B_hash_uncomment_line +B_delete_line +B_chunk_replace +B_print +B_getValueFromFile +B_getValueFromString + +B_TODO +B_TODOFlags +); +our @EXPORT = @EXPORT_OK; + + + +########################################################################### +# &B_blank_file ($filename,$pattern) blanks the file $filename, unless the +# pattern $pattern is present in the file. This lets us completely redo +# a file, if it isn't the one we put in place on a previous run... +# +# B_blank_file respects $GLOBAL_LOGONLY and uses B_open_plus and B_close_plus +# so that it makes backups and only modifies files when we're not in "-v" +# mode... +# +# If the file does not exist, the function does nothing, and gives an error +# to the Error Log +# +########################################################################### + +sub B_blank_file($$) { + + my ($filename,$pattern) = @_; + my $retval; + + # If this variable is true, we won't blank the file... + + my $found_pattern=0; + + if ($retval=&B_open_plus (*BLANK_NEW,*BLANK_OLD,$filename) ) { + + my @lines; + + while (my $line = <BLANK_OLD>) { + + push @lines,$line; + if ($line =~ $pattern) { + $found_pattern=1; + } + } + + # Only copy the old file if the new one didn't match. + if ($found_pattern) { + while ( my $line = shift @lines ) { + &B_print(*BLANK_NEW,$line); + } + } + else { + &B_log("ACTION","Blanked file $filename\n"); + } + &B_close_plus(*BLANK_NEW,*BLANK_OLD,$filename); + } + else { + &B_log("ERROR","Couldn't blank file $filename since we couldn't open it or its replacement\n"); + } + + return $retval; + +} + +########################################################################### +# &B_insert_line_after ($filename,$pattern,$line_to_insert,$line_to_follow) +# modifies $filename, inserting $line_to_insert unless one or more lines +# in the file matches $pattern. The $line_to_insert will be placed +# immediately after $line_to_follow, if it exists. If said line does not +# exist, the line will not be inserted and this routine will return 0. +# +# B_insert_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here's examples of where you might use this: +# +# You'd like to insert a line in Apache's configuration file, in a +# particular section. +# +########################################################################### + +sub B_insert_line_after($$$$) { + + my ($filename,$pattern,$line_to_insert,$line_to_follow) = @_; + + my @lines; + my $found_pattern=0; + my $found_line_to_follow=0; + + my $retval=1; + + if ( &B_open_plus (*INSERT_NEW,*INSERT_OLD,$filename) ) { + + # Read through the file looking for a match both on the $pattern + # and the line we are supposed to be inserting after... + + my $ctr=1; + while (my $line=<INSERT_OLD>) { + push (@lines,$line); + if ($line =~ $pattern) { + $found_pattern=1; + } + if ( ($found_line_to_follow < 1) and ($line =~ $line_to_follow)) { + $found_line_to_follow=$ctr; + } + $ctr++; + } + + # Log an error if we never found the line we were to insert after + unless ($found_line_to_follow ) { + $retval=0; + &B_log("ERROR","Never found the line that we were supposed to insert after in $filename\n"); + } + + # Now print the file back out, inserting our line if we should... + + $ctr=1; + while (my $line = shift @lines) { + &B_print(*INSERT_NEW,$line); + if ( ($ctr == $found_line_to_follow) and ($found_pattern == 0) ) { + &B_print(*INSERT_NEW,$line_to_insert); + &B_log("ACTION","Inserted the following line in $filename:\n"); + &B_log("ACTION","$line_to_insert"); + } + $ctr++; + } + + &B_close_plus (*INSERT_NEW,*INSERT_OLD,$filename); + + } + else { + $retval=0; + &B_log("ERROR","Couldn't insert line to $filename, since open failed."); + } + + return $retval; + +} +########################################################################### +# &B_insert_line_before ($filename,$pattern,$line_to_insert,$line_to_preceed) +# modifies $filename, inserting $line_to_insert unless one or more lines +# in the file matches $pattern. The $line_to_insert will be placed +# immediately before $line_to_preceed, if it exists. If said line does not +# exist, the line will not be inserted and this routine will return 0. +# +# B_insert_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here's examples of where you might use this: +# +# You'd like to insert a line in Apache's configuration file, in a +# particular section. +# +########################################################################### + +sub B_insert_line_before($$$$) { + + my ($filename,$pattern,$line_to_insert,$line_to_preceed) = @_; + + my @lines; + my $found_pattern=0; + my $found_line_to_preceed=0; + + my $retval=1; + + if ( &B_open_plus (*INSERT_NEW,*INSERT_OLD,$filename) ) { + + # Read through the file looking for a match both on the $pattern + # and the line we are supposed to be inserting after... + + my $ctr=1; + while (my $line=<INSERT_OLD>) { + push (@lines,$line); + if ($line =~ $pattern) { + $found_pattern=1; + } + if ( ($found_line_to_preceed < 1) and ($line =~ $line_to_preceed)) { + $found_line_to_preceed=$ctr; + } + $ctr++; + } + + # Log an error if we never found the line we were to preceed + unless ($found_line_to_preceed ) { + $retval=0; + &B_log("ERROR","Never found the line that we were supposed to insert before in $filename\n"); + } + + # Now print the file back out, inserting our line if we should... + + $ctr=1; + while (my $line = shift @lines) { + if ( ($ctr == $found_line_to_preceed) and ($found_pattern == 0) ) { + &B_print(*INSERT_NEW,$line_to_insert); + &B_log("ACTION","Inserted the following line in $filename:\n"); + &B_log("ACTION","$line_to_insert"); + } + &B_print(*INSERT_NEW,$line); + $ctr++; + } + + &B_close_plus (*INSERT_NEW,*INSERT_OLD,$filename); + + } + else { + $retval=0; + &B_log("ERROR","Couldn't insert line to $filename, since open failed."); + } + + return $retval; + +} + +########################################################################### +# &B_insert_line ($filename,$pattern,$line_to_insert,$line_to_follow) +# +# has been renamed to B_insert_line_after() +# +# This name will continue to work, as a shim for code that has not been +# transitioned. +########################################################################### + +sub B_insert_line($$$$) { + + my $rtn_value = &B_insert_line_after(@_); + + return ($rtn_value); +} + + +########################################################################### +# &B_append_line ($filename,$pattern,$line_to_append) modifies $filename, +# appending $line_to_append unless one or more lines in the file matches +# $pattern. This is an enhancement to the append_line_if_no_such_line_exists +# idea. +# +# Additionally, if $pattern is set equal to "", the line is always appended. +# +# B_append_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here's examples of where you might use this: +# +# You'd like to add a root line to /etc/ftpusers if none exists. +# You'd like to add a Options Indexes line to Apache's config. file, +# after you delete all Options lines from said config file. +# +########################################################################### + +sub B_append_line($$$) { + + my ($filename,$pattern,$line_to_append) = @_; + + my $found_pattern=0; + my $retval=1; + + if ( &B_open_plus (*APPEND_NEW,*APPEND_OLD,$filename) ) { + while (my $line=<APPEND_OLD>) { + &B_print(*APPEND_NEW,$line); + if ($line =~ $pattern) { + $found_pattern=1; + } + } + # Changed != 0 to $pattern so that "" works instead of 0 and perl + # does not give the annoying + # Argument "XX" isn't numeric in ne at ... + if ( $pattern eq "" or ! $found_pattern ) { + &B_print(*APPEND_NEW,$line_to_append); + &B_log("ACTION","Appended the following line to $filename:\n"); + &B_log("ACTION","$line_to_append"); + } + &B_close_plus (*APPEND_NEW,*APPEND_OLD,$filename); + } + else { + $retval=0; + &B_log("ERROR","# Couldn't append line to $filename, since open failed."); + } + + return $retval; + +} + +########################################################################### +# &B_prepend_line ($filename,$pattern,$line_to_prepend) modifies $filename, +# pre-pending $line_to_prepend unless one or more lines in the file matches +# $pattern. This is an enhancement to the prepend_line_if_no_such_line_exists +# idea. +# +# B_prepend_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here's examples of where you might use this: +# +# You'd like to insert the line "auth required pam_deny.so" to the top +# of the PAM stack file /etc/pam.d/rsh to totally deactivate rsh. +# +########################################################################### + +sub B_prepend_line($$$) { + + my ($filename,$pattern,$line_to_prepend) = @_; + + my @lines; + my $found_pattern=0; + my $retval=1; + + if ( &B_open_plus (*PREPEND_NEW,*PREPEND_OLD,$filename) ) { + while (my $line=<PREPEND_OLD>) { + push (@lines,$line); + if ($line =~ $pattern) { + $found_pattern=1; + } + } + unless ($found_pattern) { + &B_print(*PREPEND_NEW,$line_to_prepend); + } + while (my $line = shift @lines) { + &B_print(*PREPEND_NEW,$line); + } + + &B_close_plus (*PREPEND_NEW,*PREPEND_OLD,$filename); + + # Log the action + &B_log("ACTION","Pre-pended the following line to $filename:\n"); + &B_log("ACTION","$line_to_prepend"); + } + else { + $retval=0; + &B_log("ERROR","Couldn't prepend line to $filename, since open failed.\n"); + } + + return $retval; + +} + + +########################################################################### +# &B_replace_line ($filename,$pattern,$line_to_switch_in) modifies $filename, +# replacing any lines matching $pattern with $line_to_switch_in. +# +# It returns the number of lines it replaced (or would have replaced, if +# LOGONLY mode wasn't on...) +# +# B_replace_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here an example of where you might use this: +# +# You'd like to replace any Options lines in Apache's config file with: +# Options Indexes FollowSymLinks +# +########################################################################### + +sub B_replace_line($$$) { + + my ($filename,$pattern,$line_to_switch_in) = @_; + my $retval=0; + + if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) { + while (my $line=<REPLACE_OLD>) { + unless ($line =~ $pattern) { + &B_print(*REPLACE_NEW,$line); + } + else { + # Don't replace the line if it's already there. + unless ($line eq $line_to_switch_in) { + &B_print(*REPLACE_NEW,$line_to_switch_in); + + $retval++; + &B_log("ACTION","File modification in $filename -- replaced line\n" . + "$line\n" . + "with:\n" . + "$line_to_switch_in"); + } + # But if it is there, make sure it stays there! (by Paul Allen) + else { + &B_print(*REPLACE_NEW,$line); + } + } + } + &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename); + } + else { + $retval=0; + &B_log("ERROR","Couldn't replace line(s) in $filename because open failed.\n"); + } + + return $retval; +} + +########################################################################### +# &B_replace_lines ($filename,$patterns_and_substitutes) modifies $filename, +# replacing the line matching the nth $pattern specified in $patterns_and_substitutes->[n]->[0] +# with the corresponding substitutes in $patterns_and_substitutes->[n]->-[1] +# +# It returns the number of lines it replaced (or would have replaced, if +# LOGONLY mode wasn't on...) +# +# B_replace_lines uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here an example of where you might use this: +# +# You'd like to replace /etc/opt/ssh/sshd_config file +# (^#|^)Protocol\s+(.*)\s*$ ==> Protocol 2 +# (^#|^)X11Forwarding\s+(.*)\s*$ ==> X11Forwarding yes +# (^#|^)IgnoreRhosts\s+(.*)\s*$ ==> gnoreRhosts yes +# (^#|^)RhostsAuthentication\s+(.*)\s*$ ==> RhostsAuthentication no +# (^#|^)RhostsRSAAuthentication\s+(.*)\s*$ ==> RhostsRSAAuthentication no +# (^#|^)PermitRootLogin\s+(.*)\s*$ ==> PermitRootLogin no +# (^#|^)PermitEmptyPasswords\s+(.*)\s*$ ==> PermitEmptyPasswords no +# my $patterns_and_substitutes = [ +# [ '(^#|^)Protocol\s+(.*)\s*$' => 'Protocol 2'], +# ['(^#|^)X11Forwarding\s+(.*)\s*$' => 'X11Forwarding yes'], +# ['(^#|^)IgnoreRhosts\s+(.*)\s*$' => 'gnoreRhosts yes'], +# ['(^#|^)RhostsAuthentication\s+(.*)\s*$' => 'RhostsAuthentication no'], +# ['(^#|^)RhostsRSAAuthentication\s+(.*)\s*$' => 'RhostsRSAAuthentication no'], +# ['(^#|^)PermitRootLogin\s+(.*)\s*$' => 'PermitRootLogin no'], +# ['(^#|^)PermitEmptyPasswords\s+(.*)\s*$' => 'PermitEmptyPasswords no'] +#] +# B_replaces_lines($sshd_config,$patterns_and_substitutes); +########################################################################### + +sub B_replace_lines($$){ + my ($filename, $pairs) = @_; + my $retval = 0; + if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) { + while (my $line = <REPLACE_OLD>) { + my $switch; + my $switch_before = $line; + chomp($line); + foreach my $pair (@$pairs) { + $switch = 0; + + my $pattern = $pair->[0] ; + my $replace = $pair->[1]; + my $evalstr = '$line' . "=~ s/$pattern/$replace/"; + eval $evalstr; + if ($@) { + &B_log("ERROR", "eval $evalstr failed.\n"); + } + #if ( $line =~ s/$pair->[0]/$pair->[1]/) { + # $switch = 1; + # last; + #} + } + &B_print(*REPLACE_NEW,"$line\n"); + if ($switch) { + $retval++; + B_log("ACTION","File modification in $filename -- replaced line\n" . + "$switch_before\n" . + "with:\n" . + "$line\n"); + } + } + &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename); + return 1; + } + else { + $retval=0; + &B_log("ERROR","Couldn't replace line(s) in $filename because open failed.\n"); + } +} + +################################################################################################ +# &B_replace_pattern ($filename,$pattern,$pattern_to_remove,$text_to_switch_in) +# modifies $filename, acting on only lines that match $pattern, replacing a +# string that matches $pattern_to_remove with $text_to_switch_in. +# +# Ex: +# B_replace_pattern('/etc/httpd.conf','^\s*Options.*\bIncludes\b','Includes','IncludesNoExec') +# +# replaces all "Includes" with "IncludesNoExec" on Apache Options lines. +# +# It returns the number of lines it altered (or would have replaced, if +# LOGONLY mode wasn't on...) +# +# B_replace_pattern uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +################################################################################################# + +sub B_replace_pattern($$$$) { + + my ($filename,$pattern,$pattern_to_remove,$text_to_switch_in) = @_; + my $retval=0; + + if ( &B_open_plus (*REPLACE_NEW,*REPLACE_OLD,$filename) ) { + while (my $line=<REPLACE_OLD>) { + unless ($line =~ $pattern) { + &B_print(*REPLACE_NEW,$line); + } + else { + my $orig_line =$line; + $line =~ s/$pattern_to_remove/$text_to_switch_in/; + + &B_print(*REPLACE_NEW,$line); + + $retval++; + &B_log("ACTION","File modification in $filename -- replaced line\n" . + "$orig_line\n" . + "via pattern with:\n" . + "$line\n\n"); + } + } + &B_close_plus (*REPLACE_NEW,*REPLACE_OLD,$filename); + } + else { + $retval=0; + &B_log("ERROR","Couldn't pattern-replace line(s) in $filename because open failed.\n"); + } + + return $retval; +} + + +########################################################################### +# &B_match_line($file,$pattern); +# +# This subroutine will return a 1 if the pattern specified can be matched +# against the file specified. It will return a 0 otherwise. +# +# return values: +# 0: pattern not in file or the file is not readable +# 1: pattern is in file +########################################################################### +sub B_match_line($$) { + # file to be checked and pattern to check for. + my ($file,$pattern) = @_; + # if the file is readable then + if(-r $file) { + # if the file can be opened then + if(open FILE,"<$file") { + # look at each line in the file + while (my $line = <FILE>) { + # if a line matches the pattern provided then + if($line =~ $pattern) { + # return the pattern was found + B_log('DEBUG','Pattern: ' . $pattern . ' matched in file: ' . + $file . "\n"); + return 1; + } + } + } + # if the file cann't be opened then + else { + # send a note to that affect to the errorlog + &B_log("ERROR","Unable to open file for read.\n$file\n$!\n"); + } + } + B_log('DEBUG','Pattern: ' . $pattern . ' not matched in file: ' . + $file . "\n"); + # the provided pattern was not matched against a line in the file + return 0; +} + +########################################################################### +# &B_match_line_only($file,$pattern); +# +# This subroutine checks if the specified pattern can be matched and if +# it's the only content in the file. The only content means it's only but +# may have several copies in the file. +# +# return values: +# 0: pattern not in file or pattern is not the only content +# or the file is not readable +# 1: pattern is in file and it's the only content +############################################################################ +sub B_match_line_only($$) { + my ($file,$pattern) = @_; + + # if matched, set to 1 later + my $retval = 0; + + # if the file is readable then + if(-r $file) { + # if the file can be opened then + if(&B_open(*FILED, $file)) { + # pattern should be matched at least once + # pattern can not be mismatched + while (my $line = <FILED>) { + if ($line =~ $pattern) { + $retval = 1; + } + else { + &B_close(*FILED); + return 0; + } + } + } + &B_close(*FILED); + } + + return $retval; +} + +########################################################################### +# &B_return_matched_lines($file,$pattern); +# +# This subroutine returns lines in a file matching a given regular +# expression, when called in the default list mode. When called in scalar +# mode, returns the number of elements found. +########################################################################### +sub B_return_matched_lines($$) +{ + my ($filename,$pattern) = @_; + my @lines = (); + + open(READFILE, $filename); + while (<READFILE>) { + chomp; + next unless /$pattern/; + push(@lines, $_); + } + if (wantarray) + { + return @lines; + } + else + { + return scalar (@lines); + } +} + +########################################################################### +# &B_match_chunk($file,$pattern); +# +# This subroutine will return a 1 if the pattern specified can be matched +# against the file specified on a line-agnostic form. This allows for +# patterns which by necessity must match against a multi-line pattern. +# This is the natural analogue to B_replace_chunk, which was created to +# provide multi-line capability not provided by B_replace_line. +# +# return values: +# 0: pattern not in file or the file is not readable +# 1: pattern is in file +########################################################################### + +sub B_match_chunk($$) { + + my ($file,$pattern) = @_; + my @lines; + my $big_long_line; + my $retval=1; + + open CHUNK_FILE,$file; + + # Read all lines into one scalar. + @lines = <CHUNK_FILE>; + close CHUNK_FILE; + + foreach my $line ( @lines ) { + $big_long_line .= $line; + } + + # Substitution routines get weird unless last line is terminated with \n + chomp $big_long_line; + $big_long_line .= "\n"; + + # Exit if we don't find a match + unless ($big_long_line =~ $pattern) { + $retval = 0; + } + + return $retval; +} + +########################################################################### +# &B_hash_comment_line ($filename,$pattern) modifies $filename, replacing +# any lines matching $pattern with a "hash-commented" version, like this: +# +# +# finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd +# becomes: +# #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd +# +# Also: +# tftp dgram udp wait root /usr/lbin/tftpd tftpd\ +# /opt/ignite\ +# /var/opt/ignite +# becomes: +# #tftp dgram udp wait root /usr/lbin/tftpd tftpd\ +# # /opt/ignite\ +# # /var/opt/ignite +# +# +# B_hash_comment_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +########################################################################### + +sub B_hash_comment_line($$) { + + my ($filename,$pattern) = @_; + my $retval=1; + + if ( &B_open_plus (*HASH_NEW,*HASH_OLD,$filename) ) { + my $line; + while ($line=<HASH_OLD>) { + unless ( ($line =~ $pattern) and ($line !~ /^\s*\#/) ) { + &B_print(*HASH_NEW,$line); + } + else { + &B_print(*HASH_NEW,"#$line"); + &B_log("ACTION","File modification in $filename -- hash commented line\n" . + "$line\n" . + "like this:\n" . + "#$line\n\n"); + # while the line has a trailing \ then we should also comment out the line below + while($line =~ m/\\\n$/) { + if($line=<HASH_OLD>) { + &B_print(*HASH_NEW,"#$line"); + &B_log("ACTION","File modification in $filename -- hash commented line\n" . + "$line\n" . + "like this:\n" . + "#$line\n\n"); + } + else { + $line = ""; + } + } + + } + } + &B_close_plus (*HASH_NEW,*HASH_OLD,$filename); + } + else { + $retval=0; + &B_log("ERROR","Couldn't hash-comment line(s) in $filename because open failed.\n"); + } + + return $retval; +} + + +########################################################################### +# &B_hash_uncomment_line ($filename,$pattern) modifies $filename, +# removing any commenting from lines that match $pattern. +# +# #finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd +# becomes: +# finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd +# +# +# B_hash_uncomment_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +########################################################################### + +sub B_hash_uncomment_line($$) { + + my ($filename,$pattern) = @_; + my $retval=1; + + if ( &B_open_plus (*HASH_NEW,*HASH_OLD,$filename) ) { + my $line; + while ($line=<HASH_OLD>) { + unless ( ($line =~ $pattern) and ($line =~ /^\s*\#/) ) { + &B_print(*HASH_NEW,$line); + } + else { + $line =~ /^\s*\#+(.*)$/; + $line = "$1\n"; + + &B_print(*HASH_NEW,"$line"); + &B_log("ACTION","File modification in $filename -- hash uncommented line\n"); + &B_log("ACTION",$line); + # while the line has a trailing \ then we should also uncomment out the line below + while($line =~ m/\\\n$/) { + if($line=<HASH_OLD>) { + $line =~ /^\s*\#+(.*)$/; + $line = "$1\n"; + &B_print(*HASH_NEW,"$line"); + &B_log("ACTION","File modification in $filename -- hash uncommented line\n"); + &B_log("ACTION","#$line"); + &B_log("ACTION","like this:\n"); + &B_log("ACTION","$line"); + } + else { + $line = ""; + } + } + } + } + &B_close_plus (*HASH_NEW,*HASH_OLD,$filename); + } + else { + $retval=0; + &B_log("ERROR","Couldn't hash-uncomment line(s) in $filename because open failed.\n"); + } + + return $retval; +} + + + +########################################################################### +# &B_delete_line ($filename,$pattern) modifies $filename, deleting any +# lines matching $pattern. It uses B_replace_line to do this. +# +# B_replace_line uses B_open_plus and B_close_plus, so that the file +# modified is backed up... +# +# Here an example of where you might use this: +# +# You'd like to remove any timeout= lines in /etc/lilo.conf, so that your +# delay=1 modification will work. + +# +########################################################################### + + +sub B_delete_line($$) { + + my ($filename,$pattern)=@_; + my $retval=&B_replace_line($filename,$pattern,""); + + return $retval; +} + + +########################################################################### +# &B_chunk_replace ($file,$pattern,$replacement) reads $file replacing the +# first occurrence of $pattern with $replacement. +# +########################################################################### + +sub B_chunk_replace($$$) { + + my ($file,$pattern,$replacement) = @_; + + my @lines; + my $big_long_line; + my $retval=1; + + &B_open (*OLDFILE,$file); + + # Read all lines into one scalar. + @lines = <OLDFILE>; + &B_close (*OLDFILE); + foreach my $line ( @lines ) { + $big_long_line .= $line; + } + + # Substitution routines get weird unless last line is terminated with \n + chomp $big_long_line; + $big_long_line .= "\n"; + + # Exit if we don't find a match + unless ($big_long_line =~ $pattern) { + return 0; + } + + $big_long_line =~ s/$pattern/$replacement/s; + + $retval=&B_open_plus (*NEWFILE,*OLDFILE,$file); + if ($retval) { + &B_print (*NEWFILE,$big_long_line); + &B_close_plus (*NEWFILE,*OLDFILE,$file); + } + + return $retval; +} + +########################################################################### +# &B_print ($handle,@list) prints the items of @list to the file handle +# $handle. It logs the action and respects the $GLOBAL_LOGONLY variable. +# +########################################################################### + +sub B_print { + my $handle=shift @_; + + my $result=1; + + unless ($GLOBAL_LOGONLY) { + $result=print $handle @_; + } + + ($handle) = "$handle" =~ /[^:]+::[^:]+::([^:]+)/; + + $result; +} + + +########################################################################## +# &B_getValueFromFile($regex,$file); +# Takes a regex with a single group "()" and returns the unique value +# on any non-commented lines +# This (and B_return_matched_lines are only used in this file, though are +# probably more generally useful. For now, leaving these here serve the following +#functions: +# a) still gets exported/associated as part of the Test_API package, and +# is still availble for a couple operations that can't be deferred to the +# main test loop, as they save values so that individual tests don't have to +# recreate (copy / paste) the logic to get them. +# +# It also avoids the circular "use" if we incldued "use Test API" at the top +# of this file (Test API "uses" this file. +# Returns the uncommented, unique values of a param=value pair. +# +# Return values: +# 'Not Defined' if the value is not present or not uniquely defined. +# $value if the value is present and unique +# +########################################################################### +sub B_getValueFromFile ($$){ + my $inputRegex=$_[0]; + my $file=$_[1]; + my ($lastvalue,$value)=''; + + my @lines=&B_return_matched_lines($file, $inputRegex); + + return &B_getValueFromString($inputRegex,join('/n',@lines)); +} + +########################################################################## +# &B_getValueFromString($param,$string); +# Takes a regex with a single group "()" and returns the unique value +# on any non-commented lines +# This (and B_return_matched_lines are only used in this file, though are +# probably more generally useful. For now, leaving these here serve the following +#functions: +# a) still gets exported/associated as part of the Test_API package, and +# is still availble for a couple operations that can't be deferred to the +# main test loop, as they save values so that individual tests don't have to +# recreate (copy / paste) the logic to get them. +# +# It also avoids the circular "use" if we incldued "use Test API" at the top +# of this file (Test API "uses" this file. +# Returns the uncommented, unique values of a param=value pair. +# +# Return values: +# 'Not Unique' if the value is not uniquely defined. +# undef if the value isn't defined at all +# $value if the value is present and unique +# +########################################################################### +sub B_getValueFromString ($$){ + my $inputRegex=$_[0]; + my $inputString=$_[1]; + my $lastValue=''; + my $value=''; + + my @lines=split(/\n/,$inputString); + + &B_log("DEBUG","B_getvaluefromstring called with regex: $inputRegex and input: " . + $inputString); + foreach my $line (grep(/$inputRegex/,@lines)) { + $line =~ /$inputRegex/; + $value=$1; + if (($lastValue eq '') and ($value ne '')) { + $lastValue = $value; + } elsif (($lastValue ne $value) and ($value ne '')) { + B_log("DEBUG","getvaluefromstring returned Not Unique"); + return 'Not Unique'; + } + } + if ((not(defined($value))) or ($value eq '')) { + &B_log("DEBUG","Could not find regex match in string"); + return undef; + } else { + &B_log("DEBUG","B_getValueFromString Found: $value ; using: $inputRegex"); + return $value; + } +} + +############################################################### +# This function adds something to the To Do List. +# Arguments: +# 1) The string you want to add to the To Do List. +# 2) Optional: Question whose TODOFlag should be set to indicate +# A pending manual action in subsequent reports. Only skip this +# If there's no security-audit relevant action you need the user to +# accomplish +# Ex: +# &B_TODO("------\nInstalling IPFilter\n----\nGo get Ipfilter","IPFilter.install_ipfilter"); +# +# +# Returns: +# 0 - If error condition +# True, if sucess, specifically: +# "appended" if the append operation was successful +# "exists" if no change was made since the entry was already present +############################################################### +sub B_TODO ($;$) { + my $text = $_[0]; + my $FlaggedQuestion = $_[1]; + my $multilineString = ""; + + # trim off any leading and trailing new lines, regexes separated for "clarity" + $text =~ s/^\n+(.*)/$1/; + $text =~ s/(.*)\n+$/$1/; + + if ( ! -e &getGlobal('BFILE',"TODO") ) { + # Make the TODO list file for HP-UX Distro + &B_create_file(&getGlobal('BFILE', "TODO")); + &B_append_line(&getGlobal('BFILE', "TODO"),'a$b', + "Please take the steps below to make your system more secure,\n". + "then delete the item from this file and record what you did along\n". + "with the date and time in your system administration log. You\n". + "will need that information in case you ever need to revert your\n". + "changes.\n\n"); + } + + + if (open(TODO,"<" . &getGlobal('BFILE', "TODO"))) { + while (my $line = <TODO>) { + # getting rid of all meta characters. + $line =~ s/(\\|\||\(|\)|\[|\]|\{|\}|\^|\$|\*|\+|\?|\.)//g; + $multilineString .= $line; + } + chomp $multilineString; + $multilineString .= "\n"; + + close(TODO); + } + else { + &B_log("ERROR","Unable to read TODO.txt file.\n" . + "The following text could not be appended to the TODO list:\n" . + $text . + "End of TODO text\n"); + return 0; #False + } + + my $textPattern = $text; + + # getting rid of all meta characters. + $textPattern =~ s/(\\|\||\(|\)|\[|\]|\{|\}|\^|\$|\*|\+|\?|\.)//g; + + if( $multilineString !~ "$textPattern") { + my $datestamp = "{" . localtime() . "}"; + unless ( &B_append_line(&getGlobal('BFILE', "TODO"), "", $datestamp . "\n" . $text . "\n\n\n") ) { + &B_log("ERROR","TODO Failed for text: " . $text ); + } + #Note that we only set the flag on the *initial* entry in the TODO File + #Not on subsequent detection. This is to avoid the case where Bastille + #complains on a subsequent Bastille run of an already-performed manual + #action that the user neglected to delete from the TODO file. + # It does, however lead to a report of "nonsecure" when the user + #asked for the TODO item, performed it, Bastille detected that and cleared the + # Item, and then the user unperformed the action. I think this is proper behavior. + # rwf 06/06 + + if (defined($FlaggedQuestion)) { + &B_TODOFlags("set",$FlaggedQuestion); + } + return "appended"; #evals to true, and also notes what happened + } else { + return "exists"; #evals to true, and also + } + +} + + +##################################################################### +# &B_TODOFlags() +# +# This is the interface to the TODO flags. Test functions set these when they +# require a TODO item to be completed to get to a "secure" state. +# The prune/reporting function checks these to ensure no flags are set before +# reporting an item "secure" +# "Methods" are load | save | isSet <Question> | set <Question> | unset <Question> +# +###################################################################### + +sub B_TODOFlags($;$) { + my $action = $_[0]; + my $module = $_[1]; + + use File::Spec; + + my $todo_flag = &getGlobal("BFILE","TODOFlag"); + + &B_log("DEBUG","B_TODOFlags action: $action , module: $module"); + + if ($action eq "load") { + if (-e $todo_flag ) { + &B_open(*TODO_FLAGS, $todo_flag); + my @lines = <TODO_FLAGS>; + foreach my $line (@lines) { + chomp($line); + $GLOBAL_CONFIG{"$line"}{"TODOFlag"}="yes"; + } + return (&B_close(*TODO_FLAGS)); #return success of final close + } else { + return 1; #No-op is okay + } + } elsif ($action eq "save") { + # Make sure the file exists, else create + #Note we use open_plus and and create file, so if Bastille is + #reverted, all the flags will self-clear (file deleted) + my $flagNumber = 0; + my $flagData = ''; + foreach my $key (keys %GLOBAL_CONFIG) { + if ($GLOBAL_CONFIG{$key}{"TODOFlag"} eq "yes") { + ++$flagNumber; + $flagData .= "$key\n"; + } + } + if (not( -e $todo_flag)) { + &B_log("DEBUG","Initializing TODO Flag file: $todo_flag"); + &B_create_file($todo_flag); # Make sure it exists + } + &B_blank_file($todo_flag, + "This will not appear in the file; ensures blanking"); + return &B_append_line($todo_flag, "", "$flagData"); #return success of save + } elsif (($action eq "isSet") and ($module ne "")) { + if ($GLOBAL_CONFIG{"$module"}{"TODOFlag"} eq "yes") { + return 1; #TRUE + } else { + return 0; #FALSE + } + } elsif (($action eq "set") and ($module ne "")) { + $GLOBAL_CONFIG{"$module"}{"TODOFlag"} = "yes"; + } elsif (($action eq "clear") and ($module ne "")) { + $GLOBAL_CONFIG{"$module"}{"TODOFlag"} = ""; + } else { + &B_log("ERROR","TODO_Flag Called with invalid parameters: $action , $module". + "audit report may be incorrect."); + return 0; #FALSE + } +} + +1; + + |