fcheck/ 40755 0 0 0 7253307477 10301 5ustar rootrootfcheck/license100444 0 0 43076 7253304775 11771 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. fcheck/fcheck100555 0 0 215473 7253306036 11607 0ustar rootroot#!/usr/bin/perl ############################################################################# # # # Copyright (C) 1996 Michael A. Gumienny # # # # This program is free software; you can redistribute it and/or modify it # # under the terms of the GNU General Public License as published by the # # Free Software Foundation; either version 2 of the License, or (at your # # option) any later version. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # # Public License for more details. # # # # You should have received a copy of the GNU General Public License along # # with this program; if not, write to: # # # # Free Software Foundation, Inc. # # 59 Temple Place - Suite 330 # # Boston, MA 02111-1307, USA. # # # # Or you can find the full GNU GPL online at: http://www.gnu.org # # # # Please send your comments, updates, improvements, wishes and bug reports # # for fcheck to: # # # # Michael A. Gumienny gumienny@hotmail.com # # # ############################################################################# ############################################################################# # # # File: fcheck # # # # Usage: fcheck [-acdfihlrsvx] [configuration file] [directory] # # # # $Id: fcheck,v 2.7.59 2001/03/03 02:38:26 root Exp root $ # # # # Description: # # Used to validate creation dates of critical system files, and as # # a baseline system verification script. # # # # Options: # # -a Automatic mode, do all directories in configuration file. # # -c Create a new base line database for the given directory. # # -d Directory names are to be monitored for changes also. # # -f Use alternate 'filename' as the configuration file. # # -i Ignore creation times, check permissions, adds, deletes. # # -h Append the $HOSTNAME to the configuration filename. # # -l Log information to logger rather than stdout messages. # # -r Report mode - so you can save to a file and get a positive # # record of what you have checked, even if errors go to logger # # -s Sign each file with a CRC/hash signature. # # -v Verbose mode, not used for report generation. # # -x eXtended Unix checks - Nlinks, UID, GID, Major/Minor numers # # # # Author: Michael A. Gumienny # # # # Written: 1996 # # # ############################################################################# ############################################################################# # $Log: fcheck,v $ # Revision 2.7.59 2001/03/03 02:38:26 root # Beta testers gave final thumbs up, final checkin w/ approval. # # # Revision 2.7.58 2001/03/01 11:24:29 root # This version needed some repairs to the output. CRC differences were not being # displayed to the screen, only to logger. No impact from this bug, FCheck still # flagged CRC changes, but did not display them as CRC diffs, only that a change # had been detected when ran in interactive mode. # # # Revision 2.7.57 2001/02/21 01:18:09 root # GNU version of MD5 sends its output backwards than high end Unix systems. # Added a new routines to autodetect this based on filename, md5 or md5sum. # # # Revision 2.7.56 2001/01/13 17:26:19 root # Checked in with minor cosmetic improvements. # # # Revision 2.7.55 2000/12/05 01:18:09 root # Put back exclusion routine that was in version 2.7.51. New routine did not # accurately exlude files/directories except for exact matches only. # # # Revision 2.7.54 2000/11/07 12:33:32 root # Added "FindDiff" routine to report the detail of what has been changed, # taking all guess work out of the reported lines. # # FCheck WARNING lines now report like the following: # fcheck: "WARNING: [host] /bin/cron [CRCs: 3932278317 - 2003041699]" # which tells you immediately that there is a variation in the CRCs. # # # Revision 2.7.52Beta 2000/10/15 16:50 # Added more features, merged in several suggestions from jim moore. # (Sorry for the delay in getting this out with them.) # # The ability to include chunks of configuration files through the CFINCLUDE # directive (suggestion by John Vogtle) # # Added the "FILE" type for checking individual files. # # Made the database into a single file # # Fixed bug in MD5 handling (so that it can use standard source of MD5 # available) # # Added report mode, so that the report is sufficient for documentation # # Added modes to check uid, gid, and major and minor numbers on Unix special # files # # Added ReadDB and WriteDB configuration primatives, so that the same config # file could be used to generate a database that would be moved to read-only # a location # # Added check for users passing a (-s) flag to HASHFunc (otherwise the open # tries to execute the file and collect its output) # # # Revision 2.7.51 2000/04/02 18:45:32 root # Final checkin for revision control of tested script # # # Revision 2.7.50 2000/03/11 16:10:39 root # Modified parsing of "logger" variable to allow user defined option flags # # Finally got around to fixing the trailing space bug in the configuration # file. Now the parser is less strict of the varying editors being used to # create configuration files. # # Todo: request have been made to register a permission problem when ran # as other than root user, and can't recurse directory trees rather # than terminate with an error as fcheck does now. # # # Revision 2.7.49 2000/03/11 15:08:04 root # Fixed option when told to ignore creation dates to also check file size. # # Fixed option when told to ignore diretory names (-d), when you are not # checking recursively and don't want to see directory Inode changes. # # # Revision 2.7.47 2000/02/21 02:05:18 root # Removed the pre-defined "-t" (tag) option used by logger to allow for user # defined output devices: scritps, programs, or device files. # # Typo found under permission calculations: # local ($ftype) = $ftype[($mode & 0170000)>>12]; # # A reported glitch with European and some US spellings for filenames that # contain a single quote (D'Abo) was fixed. # # # Revision 2.7.46 2000/01/10 23:06:22 root # Minor improvements and documentation efforts made. # Replaced uneccesary date coding to compensate epoch of January 1, 1970 GMT # $year += ($year < 70) ? 2000 : 1900; # with a simpler # $year += 1900; # # Revision 2.7.45 1999/10/19 17:40:22 root # Added optional file hash/CRC signature abilities. # # Added support for multiple configuration files by passing optional name. # # Updated documentation. # # Todo: Minor issue. Need to check string parsing from configuration file. # Spaces on end of string confuse current parser logic. # # # Revision 2.7.43 1999/10/08 20:57:34 root # Added optional CRC/hash signature ability # # # Revision 2.7.42 1999/09/21 00:55:14 root # Experimental stages, checked in. # # # Revision 2.7.40 1999/09/14 02:18:01 root # Added suggestion by Ian Thurlbeck to replace the # lookup intensive arrays with associative arrays to speed things along. # # Removed a few un-needed lines and routines left in from previos edits. # # Updated documentation. # # # Revision 2.7.38 1999/08/16 22:16:37 root # Enclosed logger string in quotes. RedHat Linux logger choked without them. # Fixed spelling errors, etc in documentation. # # # Revision 2.7.37 1999/08/04 22:51:00 root # Minor changes to permissions routines. # # # Revision 2.7.34 1999/07/29 01:11:13 root # Now Works under windows, added internal ls type function. # # # Revision 2.6.27 1999/07/24 14:49:04 root # Initial checkin for migration to DOS PERL # ############################################################################# # # # User modifiable variable definitions: # # # ############################################################################# # This should be passed through the command line, but hard coding still works $config="/usr/local/admtools/conf/fcheck.cfg"; #$config="C:/Work/fcheck/fcheck.cfg"; ############################################################################# # # # Non-User modifiable variable definitions: (DO NOT MODIFY THESE!) # # # ############################################################################# undef($Auto); undef($Verbose); undef($BaseLine); undef($CreateDate); undef($DirCheck); undef($Logging); undef($DOS); undef($Hash); undef($XTended); undef($Reporting); undef($ReportFail); undef($ALogger); undef($ReadDB); undef($WriteDB); undef($GnuHASHOut); $XTended=0; $Reporting=0; $ReportFail=0; ($Me)=split("/", reverse($0)); ($Me)=split(" ", reverse($Me)); $rcfflag=1; ############################################################################# # &Help; # # This routine explains brief usage syntax to STDOUT. The program is then # # terminated. # ############################################################################# sub Help { open(ME, "<$0"); for ($i=0; $i<38; $i++) { $_=; } close(ME); $_ = substr($_, 15, 18); printf("Usage:\t%s [-acdfhilrsvx] [config filename] [directory]\n", $Me); printf("\tUsed to validate creation dates of critical system files.\n\n"); printf("\tVersion: %s\n\n", $_); printf("\tOptions:\n"); printf("\t-a\tAutomatic mode, do all directories in configuration file.\n"); printf("\t-c\tCreate base-line database.\n"); printf("\t-d\tDirectory names are to be monitored for changes also.\n"); printf("\t-f\tUse alternate 'config filename' to initiate from.\n"); printf("\t-h\tAppend the \$HOSTNAME to the configuration filename.\n"); printf("\t-i\tIgnore create dates, check permissions, additions, deletions.\n"); printf("\t-l\tLog information to logger rather than stdout messages.\n"); printf("\t-r\tReporter mode that lists individual files too.\n"); printf("\t-s\tSign each file with a CRC/hash signature.\n"); printf("\t-v\tVerbose mode.\n"); printf("\t-x\teXtended Unix checks - Nlinks, UID, GID, Major/Minor numbers.\n\n"); exit(0); } ############################################################################# # &DoConfig($config_file,$recurse,$RecurseDepth); # ############################################################################# sub DoConfig { local($CFFile,$godeep,$CLevel)=@_; local($KeyWord, $Variable); local(*CONFIG); if($Verbose) { printf("debug: (DoConfig) Drawing Config from %s\n", $CFFile); } open(CONFIG, "<$CFFile") || &Error("Can't find configuration file $CFFile"); $CLevel++; $CTemp="\t" x $CLevel ; $CString="$CTemp"."$CFFile"; push(@ConfigTree,$CString); while() { next if /^#/ || /^\n/; chop; ($KeyWord, $Variable)=m/(\w+)\s*=\s*(.*)/; # Fast hack to remove trailing spaces... $Variable =~ s/\s*$//; $KeyWord = &toupper($KeyWord); if ( $Verbose) {printf("debug: Key:\"%s\"\tValue:\"%s\"\n", $KeyWord, $Variable);} # Allow config files to be modular, included from one another # This means that the last definition wins if ( $KeyWord eq "CFINCLUDE") { if (!(-r $Variable && -s $Variable)) { &Error("CFINCLUDE $Variable in config file NOT readable or zero length"); } if (-f "$Variable" && ($godeep)) { &DoConfig("$Variable", $godeep,$CLevel); } } if ( $KeyWord eq "DIRECTORY") { push(@CheckDir, $Variable); } if ( $KeyWord eq "FILE") { push(@CheckFile, $Variable); } if ( $KeyWord eq "EXCLUSION") { if($DOS) { $Variable = &toupper($Variable); } $TVariable="$Variable"; push(@ExcludeMatrix, $TVariable); } if ( $KeyWord eq "LOGGER") { ($Logger, $LoggerFlags) = $Variable =~ m/(\S+)\s* s*(.*)\s*/; (@LogFlags)=split(' ',$LoggerFlags); if ( $Verbose) { printf("debug: Corrected LOGGER value:\"%s\"\ndebug: Corrected LOGGER FLAGs:\"%s\"\n", $Logger, $LoggerFlags); } } if ( $KeyWord eq "AUTHLOGGER") { ($ALogger, $ALoggerFlags) = $Variable =~ m/(\S+)\s* s*(.*)\s*/; (@ALogFlags)=split(' ',$ALoggerFlags); if ( $Verbose) { printf("debug: Corrected AUTHLOGGER value:\"%s\"\ndebug: Corrected AUTHLOGGER FLAGs:\"%s\"\n", $ALogger, $ALoggerFlags); } } if ( $KeyWord eq "FILETYPER") { $Filefunc = $Variable; } if ( $KeyWord eq "DATABASE") { $DBFile = $Variable; } if ( $KeyWord eq "READDB") { $ReadDB = $Variable; } if ( $KeyWord eq "WRITEDB") { $WriteDB = $Variable; } if ( $KeyWord eq "HOSTNAME") { $ThisHost = $Variable; } if (($KeyWord eq "SYSTEM") && (&toupper($Variable) eq "DOS")) { $ENV{'CMDLINE'}="null"; } if (($KeyWord eq "SYSTEM") && (&toupper($Variable) eq "UNIX")) { $ENV{'CMDLINE'}=""; $ThisOS=`uname -s -r`; chop($ThisOS); $Uname=`uname -a`; chop($Uname); } if ( $KeyWord eq "TIMEZONE") { $ENV{'TZ'}=&toupper($Variable); } if ( $KeyWord eq "SIGNATURE") { $HASHFunc = $Variable; } # if ( $KeyWord eq "GNUMD5") { $GnuHASHOut = &toupper($Variable); } } # if ($GnuHASHOut == "False") { undef($GnuHASHOut); } } ############################################################################# # &Configure; # # This routine reads in the configuration file, and initializes user # # definable variables. # ############################################################################# sub Configure { if ($Verbose) { printf("debug: Attempting to determine OS and hostname.\n"); } # Try to determine the OS that we're running on based on environment # variables that do not normally exist on Unix platforms but do in DOS. # This can be set in the config file to by pass this routine. # if ($ENV{'COMSPEC'} && $ENV{'CMDLINE'}) { # I think this system is DOS ++$DOS; # hostname not included with win3.x, win95/98, but is for NT, so... $ThisHost = $ENV{'HOSTNAME'} unless $ThisHost; } else { # I think this system is Unix based if(!$ThisHost) { $ThisHost = `uname -n`; chop($ThisHost); $ThisOS=`uname -s -r`; chop($ThisOS); $Uname=`uname -a`; chop($Uname); } } # If no hostname given, default to "localhost" as our name $ThisHost = "localhost" unless $ThisHost; if ($Verbose) { printf("debug: Set hostname to: $ThisHost.\n"); } if (($Verbose) && ($DOS)) { printf("debug: OS is DOS based.\n"); } if (($Verbose) && (!$DOS)) { printf("debug: OS is Unix based.\n"); } if ($Verbose) { printf("debug: (Configure) Reading configuration file.\n"); } # Allow for a config definition involving the $HOSTNAME if($UseHostName) { $CFInput=sprintf("%s.%s", $config, $ThisHost); } else { $CFInput=$config; } if($Verbose) { printf("debug: (Configure) Configuration file set to: %s\n", $CFInput); } $ConfigLevel = -1; # Make an attempt to find the configuration file at least one deep &DoConfig($CFInput,$rcfflag,$ConfigLevel); if (!$ENV{'TZ'}) { printf("Error: Need TZ environment configured or environment set!\n\n"); &Help; } } ############################################################################# # &Exclude($filename); # ############################################################################# sub Exclude { local($EFile)=@_; if($Verbose) { printf("debug: (Exclude) checking on %s\n", $EFile); } foreach $Line (@ExcludeMatrix) { if($DOS) { $EFile = &toupper($EFile); } if($Verbose) { printf("debug: (Exclude) against %s\n", $Line); } if ($Line =~ /\/$/) { # Sub-directory matches if ($EFile =~ /^$Line/) { if($Verbose) { printf("debug: (Exclude) directory match found, ignoring below here...\n"); } return(1); } } else { # A file matches if ($EFile eq $Line) { if($Verbose) { printf("debug: (Exclude) file match found, ignoring...\n"); } return(1); } } } return(0); } ############################################################################# # &BuildFileSeg(); # ############################################################################# sub BuildFileSeg { if ($Verbose) { printf("debug: (BuildFileSeg) building baseline %s\n", $DBfile); } printf(DB "# - - - - -> BEGIN FILES <- - - - -\n"); foreach $Line (@LiveData) { printf(DB "%s\n", $Line); } printf(DB "# - - - - -> END FILES <- - - - -\n"); return 0; } ############################################################################# # &BuildDirSeg($DirName,$recurse); # ############################################################################# sub BuildDirSeg { local($DirName,$recurse)=@_; if ($Verbose) { printf("debug: (BuildDirSeg) building baseline %s\n", $DBfile); } if ( $recurse == 1 ) { printf(DB "# - - - - -> BEGIN Directory Recursion %s <- - - - -\n", $DirName); } else { printf(DB "# - - - - -> BEGIN Directory %s <- - - - -\n", $DirName); } foreach $Line (@LiveData) { printf(DB "%s\n", $Line); } return 0; } ############################################################################# # &GetFilesDB($dir); # # This routine builds the array @BaseLineData from the database segment # ############################################################################# sub GetFilesDB { local($index,$BI,$BP,$BL,$BU,$BG,$BS,$BT,$BN,$CRC,$pdir); if($DOS) { $Dirname =~ s/:/.drive/; } # Handle drive delimiters, C:, D:, E:, etc. %BaseLineData = (); if (($FilesBegin != -1) && ($FilesEnd != -1) && ($FilesEnd <= $FilesEnd)) { for ($index=$FilesBegin; $index<$FilesEnd + 1; $index++) { $_="$Database[$index]"; chop; if($Verbose) { printf("debug: (GetFilesDB) reading [%s]\n", $_); } # BI - Baseline Inode, BP - Baseline Permissions, BS - Size # BT - Time, BN - Name (all generated by the "stat" call of perl # B_CRC - the Checksum/Signature # eXtended format has $BL - Link Count, $BU - Uid, $BG - Gid if ($XTended) { ($BI, $BP, $BL, $BU, $BG, $BS, $BT, $BN, $CRC) = split("!", $_); } else { ($BI, $BP, $BS, $BT, $BN, $CRC) = split("!", $_); } if(((($BP & 0170000)>>12)==4) ) { next; } $BaseLineData{$BN} = $_; } } else { if($Verbose) { printf("debug: (GetFilesDB) No files to retrieve from DB"); } } return(@BaseLineData); } ############################################################################# # &GetLiveFiles(); # # This routine builds the array @LiveData based on the live data. # ############################################################################# sub GetLiveFiles { local($filename); if ($Verbose) { printf("debug: (GetLiveFiles) reading %s files\n",$#CheckFile ); } if ( $#CheckFile != -1 ) { for ($index=0; $index < $#CheckFile + 1; $index++) { if ($Verbose) { printf("debug: (GetLiveFiles) reading %s \n",$CheckFile[$index]); } # Report configuration problems - non-existant files - when Baselining if (!-e "$CheckFile[$index]" && $BaseLine) { if ($Logging) { if(!$DOS) { $cmd=sprintf("\"CONFIG: File %s on %s contains reference to non-existant file %s\"\n", $config,$ThisHost,$CheckFile[$index]); system($Logger, @LogFlags, $cmd); } } if ((!$Logging) || (($Logging) && ($Reporting))) { printf("\nCONFIG: File %s on %s contains reference to non-existant file %s\n",$config,$ThisHost,$CheckFile[$index]); } } # Root generally can not read a file if it is a link and the link points to # a non-existant file. Basically the stat will fail next if (!-r "$CheckFile[$index]" ); if (-d "$CheckFile[$index]" ) { push(@CheckDir, "$CheckFile[$index]" ); if ($Logging) { $cmd=sprintf("\"CONFIG: Configuration file on %s labels %s as a file when it is a directory\"\n",$ThisHost,$CheckFile[$index]); system($Logger, @LogFlags, $cmd); } next; } elsif (-f "$CheckFile[$index]" ) { &GetFileInfo("$CheckFile[$index]"); } elsif (!$DOS) { # give the Unix folks a bit more info if (-p "$CheckFile[$index]" || -S "$CheckFile[$index]" || -b "$CheckFile[$index]" || -c "$CheckFile[$index]") { &GetFileInfo("$CheckFile[$index]"); } } else { if ($Logging) { $cmd=sprintf("\"CONFIG: Configuration file on %s labels %s as a file when it is not a file or a directory\"\n",$ThisHost,$CheckFile[$index]); system($Logger, @LogFlags, $cmd); } next; } } } else { if ($Verbose) { printf("debug: (GetLiveFiles) No individual files to read\n"); } } } ############################################################################# # &GetDirDB($dir); # # This routine builds the array @BaseLineData from the database segment # ############################################################################# sub GetDirDB { local($Dirname)=@_; local($index,$BI,$BP,$BL,$BU,$BG,$BS,$BT,$BN,$CRC); if($DOS) { $Dirname =~ s/:/.drive/; } # Handle drive delimiters, C:, D:, E:, etc. $Dirname =~ s/\//_/g; if ($Verbose) { printf("debug: (GetDirDB) reading \n", $Dirname); } %BaseLineData = (); if ((defined($begin{"$Dirname"})) && (defined($end{"$Dirname"})) && ($begin{"$Dirname"} <= $end{"$Dirname"})) { for ($index=$begin{"$Dirname"}; $index<$end{"$Dirname"} + 1; $index++) { $_="$Database[$index]"; chop; if($Verbose) { printf("debug: (GetDirDB) reading [%s]\n", $_); } # BI - Baseline Inode, BP - Baseline Permissions, BS - Size # BT - Time, BN - Name (all generated by the "stat" call of perl # B_CRC - the Checksum/Signature if ($XTended) { ($BI, $BP, $BL, $BU, $BG, $BS, $BT, $BN, $CRC) = split("!", $_); } else { ($BI, $BP, $BS, $BT, $BN, $CRC) = split("!", $_); } if(!$DirCheck) { next if ( $BP =~ /^d/); } if( (!$DirCheck) && (!$DOS) && ((($BP & 0170000)>>12)==4) ) { next; } $BaseLineData{$BN} = $_; } } else { if (($Logging) && ($BaseLine)) { if(!$DOS) { $cmd=sprintf("\"CONFIG: %s database %s does NOT match config file %s on %s for %s\"\n", $Me,$DBFile,$config,$ThisHost,$Dirname); system($Logger, @LogFlags, $cmd); } } &Error("Error: Baseline does not match configuration file on $Dirname"); } return(@BaseLineData); } ############################################################################# # &GetLiveDir($dir); # # This routine builds the array @LiveData based on the live data. # ############################################################################# sub GetLiveDir { local($Dir)=@_; local($filename); if ($Verbose) { printf("debug: (GetLiveDir) reading %s\n", $Dir); } if (! -d $Dir ) { if ($Logging) { if(!$DOS) { $cmd=sprintf("\"CONFIG: Configuration file %s on %s declares %s a Directory when it is not\"\n", $config,$ThisHost,$Dir); system($Logger, @LogFlags, $cmd); } } else { printf("\nCONFIG: Configuration file %s on %s labels %s as a directory when it is NOT \n",$config,$ThisHost,$Dir); } return; } else { # It's a directory if ($Dir =~ /:\/$/) { &GetDir($Dir, 0); } elsif (($Dir =~ /\/$/) && ($Dir ne "/")) { $Dir =~ s/\/$//; &GetDir($Dir, 1); } else { &GetDir($Dir, 0); } } } ############################################################################### # $x=&Scan_Build(type); # # This is the heart of the script. Scan_Build will either scan and/or build a # # grouping of files based on the files or directories given it in the config # # file works on a directory by directory basis or a list of individual files # ############################################################################### sub Scan_Build { local($type)=@_; local($Hacks); # Check the type and log what we are doing verbose / debugging if (("$type" eq "Files") && ($Verbose)) { printf("debug: (Scan_Build) reading Files \n"); } elsif (("$type" eq "Directory") && ($Verbose)) { printf("debug: (Scan_Build) reading directory %s\n", $Dir); } elsif (("$type" ne "Files") && ("$type" ne "Directory")) { &Error("Error: Scan_Build received an invalid type $type -- Must be Files or Directory"); } # Hacks is where plusses, minuses, and changes get incremented $Hacks=0; # If we are not building, then we are scanning if (!$BaseLine) { if (("$type" eq "Files")) { # Scan the individual files # Load the segment of an existing database into an associative array BaseLineData &GetFilesDB(); # Build a similar array from the directory as it exists now &GetLiveFiles(); } else { # Scan directory # Load the segment of an existing database into an associative array BaseLineData &GetDirDB($Dir); # Build a similar array from the directory as it exists now &GetLiveDir($Dir); } # Loop over the live data to detect differences for ($ldd=0; $ldd < $#LiveData + 1; $ldd++) { if ($XTended) { ($L_Inode, $L_Perms, $L_Links, $L_Uid, $L_Gid, $L_Size, $L_Time, $L_Name, $L_CRC) = split("!", $LiveData[$ldd]); } else { ($L_Inode, $L_Perms, $L_Size, $L_Time, $L_Name, $L_CRC) = split("!", $LiveData[$ldd]); } # Ignore any not in BaseLineData next unless defined($BaseLineData{$L_Name}); if ($XTended) { ($B_Inode, $B_Perms, $B_Links, $B_Uid, $B_Gid, $B_Size, $B_Time, $B_Name, $B_CRC) = split("!", $BaseLineData{$L_Name}); } else { ($B_Inode, $B_Perms, $B_Size, $B_Time, $B_Name, $B_CRC) = split("!", $BaseLineData{$L_Name}); } # In the next version the checks, and the logging will be built through a mask if ($CreateDate) { # Ignore creation dates # Check to see if there is a mismatch in either the permissions or inode number if ((!$Hash) && (($L_Perms ne $B_Perms) || ($L_Inode ne $B_Inode) || ($L_Size ne $B_Size))) { # Are we logging (Unix style logger command) if($Logging) { # Form the command in the $cmd variable $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms), $B_Size, &ctime($B_Time), "UID", "GID", "LINKS", "CRC", $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", "CRC"), $L_Name); system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { # Logger not available - print to screen printf("\n\tWARNING: [%s] %s\n\t[%s]\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms), $B_Size, &ctime($B_Time), "UID", "GID", "LINKS", "CRC", $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", "CRC"), $L_Name); } # Increment the number of "Hacks" for mismatched permissions or inode numbers ++$Hacks; } # Additionally check for Hash (e.g. MD5) mismatches # Do we care elsif ( ($Hash) && (($L_CRC ne $B_CRC) || ($L_Perms ne $B_Perms) || ($L_Inode ne $B_Inode) || ($L_Size ne $B_Size))) { if($Logging) { # Show the change to CRC/Signature $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), "UID", "GID", "LINKS", $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", $L_CRC), $L_Name); system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { printf("\n\tWARNING: [%s] %s\n\t[%s]\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), "UID", "GID", "LINKS", $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", $L_CRC), $L_Name); } # Increment the number of "Hacks" for mismatched hashes ++$Hacks; } elsif ($Reporting) { if ( ("$type" eq "Files") || (-d $L_Name)) { printf("No changes on %s to: %s \n", $ThisHost, $L_Name); } } } elsif ($XTended) { # Creation time mode - Check times & permissions & inodes if ((!$Hash) && (($L_Time ne $B_Time) || ($L_Size ne $B_Size) || ($L_Perms ne $B_Perms) || ($L_Inode ne $B_Inode) || ($L_Links ne $B_Links) || ($L_Uid ne $B_Uid) || ($L_Gid ne $B_Gid) )) { # Log differences if($Logging) { $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), $B_Uid, $B_Gid, $B_Links, $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), $L_Uid, $L_Gid, $L_Links, $L_CRC), $L_Name); system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { printf("\n\tWARNING: [%s] %s\n\t[%s]\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), $B_Uid, $B_Gid, $B_Links, $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), $L_Uid, $L_Gid, $L_Links, $L_CRC), $L_Name); } # Increment # of changes ++$Hacks; } # Check hashes in addition to times & permissions & inodes elsif (($Hash) && (($L_Time ne $B_Time) || ($L_Size ne $B_Size) || ($L_Perms ne $B_Perms) || ($L_Inode ne $B_Inode) || ($L_Links ne $B_Links) || ($L_Uid ne $B_Uid) || ($L_Gid ne $B_Gid) || ($L_CRC ne $B_CRC))) { if($Logging) { # Show the change to CRC/Signature $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), $B_Uid, $B_Gid, "LINKS", $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), $L_Uid, $L_Gid, "LINKS", $L_CRC), $L_Name); system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { printf("\n\tWARNING: [%s] %s\n\t[%s]\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), $B_Uid, $B_Gid, "LINKS", $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), $L_Uid, $L_Gid, "LINKS", $L_CRC), $L_Name); } # End Logging # Increment # of changes ++$Hacks; } # End Hash check elsif ($Reporting) { if ( ("$type" eq "Files") || (-d $L_Name)) { printf("No changes on %s to: %s \n", $ThisHost, $L_Name); } } } else { # Creation time mode - Check times & permissions & inodes if ((!$Hash) && (($L_Time ne $B_Time) || ($L_Size ne $B_Size) || ($L_Perms ne $B_Perms) || ($L_Inode ne $B_Inode) )) { # Log differences if($Logging) { $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), "UID", "GID", "LINKS", "CRC", $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", "CRC"), $L_Name); system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { printf("\n\tWARNING: [%s] %s\n\t[%s]\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), "UID", "GID", "LINKS", "CRC", $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", "CRC"), $L_Name); } # Increment # of changes ++$Hacks; } # Check hashes in addition to times & permissions & inodes elsif (($Hash) && (($L_Time ne $B_Time) || ($L_Size ne $B_Size) || ($L_Perms ne $B_Perms) || ($L_Inode ne $B_Inode) || ($L_CRC ne $B_CRC))) { if($Logging) { # Change to CRC/Signature $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), "UID", "GID", "LINKS", $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", $L_CRC), $L_Name); system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { printf("\n\tWARNING: [%s] %s\n\t[%s]\n", $ThisHost, $L_Name, &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), "UID", "GID", "LINKS", $B_CRC, $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), "UID", "GID", "LINKS", $L_CRC), $L_Name); } # Increment # of changes ++$Hacks; } elsif ($Reporting) { if (("$type" eq "Files") || (-d $L_Name)) { printf("No changes on %s to: %s \n", $ThisHost, $L_Name); } } } # Delete matches in BaseLineData and LiveData so that the values # that remain are added or deleted files $BaseLineData{$L_Name} = ""; $LiveData[$ldd] = ""; } # loop over remaining LiveData elements (ones matching entries in # BaseLineData set to empty string) # Check for files that have been added foreach $Live (@LiveData) { next if ($Live eq ""); if ($XTended) { ($Inode, $Perms, $NLinks, $Uid, $Gid, $Size, $Time, $Name, $CRC) = split("!", $Live);} else { ($Inode, $Perms, $Size, $Time, $Name, $CRC) = split("!", $Live); } if($Logging) { if ($XTended) { $cmd=sprintf("\"ADDITION: [%s] %s [%s %s %s %s %s %s %s %s]\"\n", $ThisHost, $Name, $Inode, &ShowPerms($Perms), $NLinks, $Uid, $Gid, $Size, &ctime($Time)); } else { $cmd=sprintf("\"ADDITION: [%s] %s [%s %s %s %s]\"\n", $ThisHost, $Name, $Inode, &ShowPerms($Perms), $Size, &ctime($Time)); } system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { if ($XTended) { printf("\n\tADDITION: [%s] %s\n", $ThisHost, $Name); printf("\tInode\tPermissons\tNLink\tUid\tGid\tSize\tCreated On\n"); printf("\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $Inode, &ShowPerms($Perms), $NLinks, $Uid, $Gid, $Size, &ctime($Time)); } else { printf("\n\tADDITION: [%s] %s\n", $ThisHost, $Name); printf("\tInode\tPermissons\tSize\tCreated On\n"); printf("\t%s\t%s\t%s\t%s\n", $Inode, &ShowPerms($Perms), $Size, &ctime($Time)); } } ++$Hacks; } # Check for files that have been deleted foreach $Base (sort values %BaseLineData) { next if ($Base eq ""); if ($XTended) { ($Inode, $Perms, $NLinks, $Uid, $Gid, $Size, $Time, $Name, $CRC) = split("!", $Base); } else { ($Inode, $Perms, $Size, $Time, $Name, $CRC) = split("!", $Base); } if($Logging) { if ($XTended) { $cmd=sprintf("\"DELETION: [%s] %s [%s %s %s %s %s %s %s]\"\n", $ThisHost, $Name, $Inode, &ShowPerms($Perms), $NLinks, $Uid, $Gid, $Size, &ctime($Time)); } else { $cmd=sprintf("\"DELETION: [%s] %s [%s %s %s %s]\"\n", $ThisHost, $Name, $Inode, &ShowPerms($Perms), $Size, &ctime($Time)); } system($Logger, @LogFlags, $cmd); } if ((!$Logging) || (($Logging) && ($Reporting))) { if ($XTended) { printf("\n\tDELETION: [%s] %s\n", $ThisHost, $Name); printf("\tInode\tPermissons\tNLink\tUid\tGid\tSize\tCreated On\n"); printf("\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $Inode, &ShowPerms($Perms), $NLinks, $Uid, $Gid, $Size, &ctime($Time)); } else { printf("\n\tDELETION: [%s] %s\n", $ThisHost, $Name); printf("\tInode\tPermissons\tSize\tCreated On\n"); printf("\t%s\t%s\t%s\t%s\n", $Inode, &ShowPerms($Perms), $Size, &ctime($Time)); } } ++$Hacks; } # Tally failures, compare against 0 to set the return $ReportFail += $Hacks; } else #Build baseline database { if (("$type" eq "Files")) { # Build a similar array from the directory as it exists now &GetLiveFiles(); &BuildFileSeg(); } else { # Build a similar array from the directory as it exists now &GetLiveDir($Dir); &BuildDirSeg($Dir); } undef(@BaseLineData); undef(@LiveData); return(1); } return($Hacks); } ############################################################################### # &SpecialFileProps($FileName); # # This routine will get the properties of *nix special files, beginning with # # block and character special files # ############################################################################### sub SpecialFileProps { local ($filename,$Rdev)=@_; local ($properties,$sfptmp,$majmin,$ai,@scratchpad); if ((-b "$filename" ) || (-c "$filename" )) { $majmin=$Rdev; if (defined($Filefunc)) { $sfptmp = &ExecHash($Filefunc, $filename); (@scratchpad) = split(' ',$sfptmp); # it is the one after special, sans the parens SCRATCH: for ($ai=0; $ai < $#scratchpad + 1; $ai++) { if ( $scratchpad[$ai] =~ m/special/i ) { $majmin = $scratchpad[$ai + 1]; $majmin = substr($majmin,1,length($majmin) -2); last SCRATCH; } } } # If Filefunc not defined, just return rdev $properties = $majmin; } # Return properties $properties; } ############################################################################### # &GetFileInfo($FileName); # # This routine will build the @LiveData array from the information # # it is the guts of the old GetDir, so that individual files can be checked # ############################################################################### sub GetFileInfo { local($filename,@junk)=@_; local($Dev,$Inode,$Perms,$NLink,$Uid,$Gid,$Rdev,$Size,$ATime,$MTime,$CTime,$BlkSize,$Blocks); local($majmin); if ($Verbose) { printf("debug: (GetFileInfo) processing %s \n",$filename); } # DOS chokes when the root directory gets a double slash prepended $filename =~ s/\/\//\//; # 0 dev device number of filesystem # 1 ino inode number # 2 mode file mode (type and permissions) # 3 nlink number of (hard) links to the file # 4 uid numeric user ID of file's owner # 5 gid numeric group ID of file's owner # 6 rdev the device identifier (special files only) # 7 size total size of file, in bytes # 8 atime last access time since the epoch # 9 mtime last modify time since the epoch # 10 ctime inode change time (NOT creation time!) since the epoch # 11 blksize preferred block size for file system I/O # 12 blocks actual number of blocks allocated # (The epoch was at 00:00 January 1, 1970 GMT.) Fixed Y2K glitch under DOS next if (&Exclude($filename)); ($Dev,$Inode,$Perms,$NLink,$Uid,$Gid,$Rdev,$Size,$ATime,$MTime,$CTime,$BlkSize,$Blocks) = stat("$filename"); # Handle links and special files a little bit differently if($DOS) { if($Hash && !-d "$filename" && !-l "$filename") { $filesig = &ExecHash($HASHFunc, $filename); if ($HASHFunc =~ m/md5sum/) # Format of md5sum is # filesig filename { ($filesig) = split(" ", $filesig); } elsif ($HASHFunc =~ m/md5/) # Format of MD5 is # MD5 (filename) = filesig { ($md5label,$md5filename,$md5equals,$filesig) = split(" ", $filesig); } else # Format of most others is # filesig filename { ($filesig) = split(" ", $filesig); } # close(IN); } else { $filesig = "NOCRC"; } # Do not check Directories unless "-d" was specified # if (((($Perms & 0170000) >> 12) != 4) || $DirCheck) { # DOS names are really all uppercase, so... if($DOS) { push(@LiveData, join("!", " ", " ", $Size, $CTime, &toupper($filename), $filesig )); } else { push(@LiveData, join("!", $Inode, $Perms, $Size, $CTime, $filename, $filesig)); } if (($Verbose) && (!$DOS)) { printf("debug: (GetFileInfo) %s %s %s %s %s %s %s\n", $Inode, $Perms, $Month, $Day, $Time, $filename, $filesig); } if (($Verbose) && ($DOS)) { printf("debug: (GetFileInfo) %s %s %s %s\n", $Size, $Time, $filename, $filesig); } } } else { if($Hash) { # Eliminate things that we don't want to "open" for reading if (-p "$filename" ) { $filesig = "FIFO"; } elsif (-S "$filename" ) { $filesig = "SOCKET"; } elsif (-b "$filename" ) { $majmin = &SpecialFileProps($filename,$Rdev); $filesig = "BlockSpecial".':'.$majmin ; } elsif (-c "$filename" ) { $majmin = &SpecialFileProps($filename,$Rdev); $filesig = "CharSpecial".':'.$majmin; } elsif (-d "$filename" ) { $filesig = "Dir"; } elsif (-f "$filename" && !-s "$filename" ) { $filesig = "ZeroLength"; } elsif (!-e "$filename" ) { $filesig = "NoSuchFile"; } # OK now start looking at signatures elsif (-l "$filename" && -s "$filename") { $filesig = &ExecHash($HASHFunc, $filename); $fullsigmsg="$filesig"; # Check for can't be opened message if ( $fullsigmsg !~ m/can.t be opened/) { if ($HASHFunc =~ m/md5sum/) # Format of md5sum is # filesig filename { ($filesig) = split(" ", $filesig); } elsif ($HASHFunc =~ m/md5/) # Format of MD5 is # MD5 (filename) = filesig { ($md5label,$md5filename,$md5equals,$filesig) = split(" ", $filesig); } else # Format of most others is # filesig filename { ($filesig) = split(" ", $filesig); } } else { $filesig="CanNotOpen"; } $filesig="SYMLINK:"."$filesig"; } elsif (-f "$filename" && -s "$filename") { $filesig = &ExecHash($HASHFunc, $filename); $fullsigmsg="$filesig"; # Check for can't be opened message if ( $fullsigmsg !~ m/can.t be opened/) { if ($HASHFunc =~ m/md5sum/) # Format of md5sum is # filesig filename { ($filesig) = split(" ", $filesig); } elsif ($HASHFunc =~ m/md5/) # Format of MD5 is # MD5 (filename) = filesig { ($md5label,$md5filename,$md5equals,$filesig) = split(" ", $filesig); } else # Format of most others is # filesig filename { ($filesig) = split(" ", $filesig); } } else { $filesig="CanNotOpen"; } } else { $filesig = "NOCRC"; } } else { $filesig = "NOCRC"; } # DOS names are really all uppercase, so... if($DOS) { push(@LiveData, join("!", " ", " ", $Size, $CTime, &toupper($filename), $filesig )); } else { # Match ls -li format if($XTended) { push(@LiveData, join("!", $Inode, $Perms, $NLink, $Uid, $Gid, $Size, $CTime, $filename, $filesig)); } else { push(@LiveData, join("!", $Inode, $Perms, $Size, $CTime, $filename, $filesig)); } } if (($Verbose) && (!$DOS)) { if($XTended) { printf("debug: (GetFileInfo) %s %s %s %s %s %s %s %s\n", $Inode, $Perms, $NLink, $Uid, $Gid, $CTime, $filename, $filesig); } else { printf("debug: (GetFileInfo) %s %s %s %s %s\n", $Inode, $Perms, $CTime, $filename, $filesig); } } if (($Verbose) && ($DOS)) { printf("debug: (GetFileInfo) %s %s %s %s\n", $Size, $CTime, $filename, $filesig); } } } ############################################################################### # &GetDir($dir, $recurse); # # This routine will build the @LiveData array from the information in $dir, # # optionally this routine will recurse down that directory tree. # ############################################################################### sub GetDir { local($rootdir, $r)=@_; local($filename); # Is it a directory? if (-d "$rootdir") { opendir(DIR, $rootdir) || die "debug: (GetDir) No can do ($rootdir)...\n"; foreach (sort readdir(DIR)) { next if (/^\.\.?$/); $filename = $_; $filename = "$rootdir/$filename"; # Root generally can not read a file if it is a link and the link points to # a non-existant file. Basically the stat will fail next if (!-r "$filename" ); # DOS chokes when the root directory gets a double slash prepended if (!-d "$filename" || (-d "$filename" && $DirCheck )) {&GetFileInfo("$filename");} if ((-d "$filename" && !-l "$filename") && ($r)) { &GetDir("$filename", 1); } } close(DIR); } else { &GetFileInfo("$rootdir"); } } ############################################################################### # $success=&BuildDBIndex(); # # This small support routine will return the $string in uppercase format. # ############################################################################### sub BuildDBIndex { # Data format so that it is both human readible and is all in one file # and accomodates individual file # Format # First Line: # - Host *hostname* # Second Line: # - OS *OS* # Third Line: # - Database Creation *date* # Fourth Line is for *IX and is the results of "uname -a" # Fourth Line: # - Uname *uname* # Fifth Line: # - FCheck vx.x.x by Michael A. Gumienny # # Individual files are at the Beginning with the following delimiters # - - - - -> BEGIN FILES < - - - - - # - - - - -> END FILES < - - - - - # # Directories boundaries are delimited by # - - - - -> BEGIN Directory *directory_name* < - - - - - # or # - - - - -> BEGIN Directory Recursion *directory_name* < - - - - - # # Recursion is only documented, so that the if the config file changes # # The last line in the file should be # - - - - -> END Directories < - - - - - # local($index); if ($Verbose) { printf("debug: (BuildDBIndex) processing %s \n",$filename); } $DirFromDB = "initial"; $FoundEndDir = 2 ; $FoundEndFile = 2 ; $FilesBegin = -1; $FilesEnd = -1; for ($index=0; ($Done == 0) && ($index<$#Database + 1); $index++) { # Skip header info next if ($Database[$index] =~ m/^#(-|\s|>)*Database\s*Creation\s*/) ; next if ($Database[$index] =~ m/^#(-|\s|>)*Host\s*/) ; next if ($Database[$index] =~ m/^#(-|\s|>)*OS\s*/) ; next if ($Database[$index] =~ m/^#(-|\s|>)*Uname\s*/) ; next if ($Database[$index] =~ m/^#(-|\s|>)*FCheck\s*/) ; if ($Database[$index] =~ m/^#(-|\s|>)*BEGIN\s*FILES\s*/) { $FoundEndFile = 0 ; if ($Verbose) { printf("debug: (BuildDBIndex) Individual Files declaration at line %s\n",$index); } # Define the elements into the "files" array $FilesBegin = $index + 1; } elsif ($Database[$index] =~ m/^#(-|\s|>)*END\s*FILES\s*/) { $FilesEnd = $index - 1 ; $FoundEndFile = 1 ; # Begin processing directories or files } elsif ($Database[$index] =~ m/^#(-|\s|>)*BEGIN\s*Directory\s*/) { # Detect delimiter problems if ($FoundEndFile == 0) { &Error("Malformed database no ending delimiter line for FILES" ); } # Store last line of previous directory if ($DirFromDB ne "initial") { $end{"$DirFromDB"} = $index - 1 }; $FoundEndDir = 0 ; $tmp = "$Database[$index]"; chop($tmp); # Chop up the line $tmp =~ s/^#(-|\s|>)*BEGIN\s*Directory\s*// ; if ($tmp =~ m/^\s*Recursion\s*/) { $tmp2 = "$tmp"; $tmp2 =~ s/^\s*Recursion\s*//; $tmp2 =~ s/(-|\s|>|<)*$//; # From original fcheck - DOS may have heartburn $tmp2 =~ s/\//_/g; $DirFromDB = "$tmp2"; $recurse["$DirFromDB"] = 1; if ($Verbose) { printf("debug: (BuildDBIndex) Recursive Directory declaration for %s at line %s \n",$DirFromDB,$index); } } else { $tmp =~ s/(-|\s|>|<)*$//; # From original fcheck - DOS may have heartburn $tmp =~ s/\//_/g; $DirFromDB = "$tmp"; if ($Verbose) { printf("debug: (BuildDBIndex) Directory declaration for %s at line %s \n",$DirFromDB,$index); } } $begin{"$DirFromDB"} = $index + 1; } elsif ($Database[$index] =~ m/^#(-|\s|>)*END\s*Directories\s*/) { if ($DirFromDB ne "initial") { $end{"$DirFromDB"} = $index - 1 }; $FoundEndDir = 1 ; } } # Detect delimiter problems if ($FoundEndDir == 0) { &Error("Malformed database no ending delimiter line for Directories" ); } } ############################################################################### # $x=&toupper($string); # # This small support routine will return the $string in uppercase format. # ############################################################################### sub toupper { local($x) = @_; $x =~ tr/a-z/A-Z/; return $x; } ############################################################################### # $x=&ctime($y); # # This support routine will return the converted time to human readable format# # Basically, I'm trying to get away from any functions that may not be in any # # very minimal PERL distribution. # ############################################################################### sub ctime { local($time) = @_; local($[) = 0; local($sec, $min, $hour, $mday, $mon, $year, $wday); @WeekDay = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); @Month = ('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'); ($sec, $min, $hour, $mday, $mon, $year, $wday) = ($TZ) ? gmtime($time) : localtime($time); if ($DOS) { # Present the times in the traditional DOS way... sprintf("%02d-%02d-%02d %02d:%02d%s", $mon, $mday, $year, $hour, $min, ($hour >=12) ? "p" : "a"); } else { # Present the times in the traditional Unix way... # Will anybody still be using this after 2036? #$year += ($year < 70) ? 2000 : 1900; $year += 1900; sprintf("%s %02d %02d:%02d %4d", $Month[$mon], $mday, $hour, $min, $year); } } ############################################################################### # $x=&ShowPerms($y); # # This routine is a fairly simplistic approach (Hey, it works!) to converting # # the returned "stat" call values to the more readable "rwx" format of Unix. # ############################################################################### sub ShowPerms { local($mode) = @_; if($DOS) { return("\t"); } local(@perms) = ("---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"); local(@ftype) = ("?", "p", "c", "?", "d", "?", "b", "?", "-", "?", "l", "?", "s", "?", "?", "?"); local ($setids) = ($mode & 07000)>>9; local (@permstrs) = @perms[($mode & 0700) >> 6, ($mode & 0070) >> 3, ($mode & 0007) >> 0]; local ($ftype) = $ftype[($mode & 0170000)>>12]; if ($setids) { # Sticky Bit? if ($setids & 01) { $permstrs[2] =~ s/([-x])$/$1 eq 'x' ? 't' : 'T'/e; } # Setuid Bit? if ($setids & 04) { $permstrs[0] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e; } # Setgid Bit? if ($setids & 02) { $permstrs[1] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e; } } return (join('', $ftype, @permstrs)); } ############################################################################### # &Error("string"); # # This routine prints out critical errors and terminates execution. # ############################################################################### sub Error { printf("%s: %s\nterminating...\n\n", $Me, @_); exit(2); } ############################################################################### # $x=&FindDiff($LInode, $LPerms, $LSize, $LTime, $Luid, $Lgid, $LLinks, $LCRC,# # $CInode, $CPerms, $CSize, $CTime, $Cuid, $Cgid], $CLinks, $CCRC); # # This routine will determine the differences between the baseline database # # and the current run, and return only thoses differences. # ############################################################################### sub FindDiff { local($C); local($LI, $LP, $LS, $LT, $LU, $LG, $LL, $LC, $CN, $CI, $CP, $CS, $CT, $CU, $CG, $CL, $CC)=@_; if ($LI ne $CI) { $C = sprintf("Inodes: %s - %s, ", $LI, $CI); } if ($LP ne $CP) { $C = sprintf("%sPermissions: %s - %s, ", $C, $LP, $CP); } if ($LS ne $CS) { $C = sprintf("%sSizes: %s - %s, ", $C, $LS, $CS); } if ($LT ne $CT) { $C = sprintf("%sTimes: %s - %s, ", $C, $LT, $CT); } if ($LU ne $CU) { $C = sprintf("%sUIDs: %s - %s, ", $C, $LU, $CU); } if ($LG ne $CG) { $C = sprintf("%sGIDs: %s - %s, ", $C, $LG, $CG); } if ($LL ne $CL) { $C = sprintf("%sLinks: %s - %s, ", $C, $LL, $CL); } if ($LC ne $CC) { $C = sprintf("%sCRCs: %s - %s, ", $C, $LC, $CC); } #printf("WARNING: %s\n", $C); # $cmd = sprintf("\"WARNING: [%s] %s [%s]\"\n", $ThisHost, $L_Name, # &FindDiff($B_Inode, &ShowPerms($B_Perms),$B_Size, &ctime($B_Time), # $B_Name, $L_Inode, &ShowPerms($L_Perms),$L_Size, &ctime($L_Time), $L_Name)); # system($Logger, @LogFlags, $cmd); # Quick hack to get this released on time. chop($C); chop($C); return($C); } ############################################################################### # $Hash=&ExecHash($HASHFunc, $filename); # # This routine allows us to do a safer hash spawn without generating a shell. # # This routine is also used by the special properties routine for major/minor # # number determinations. # # # # This also provides a springboard for future expansion of a possible internal# # MD5 routine, etc... # ############################################################################### sub ExecHash { local($HASHFunc, $filename) = @_; local $filesig; unless ( defined $HASHFunc ) { &Error("invalid command $HASHFunc given"); } if (open (HASHIN, "-|") ) { $filesig = ; close HASHIN; } else { exec ($HASHFunc, $filename); } unless ( $filesig ) { &Error("$HASHFunc was unable to read $filename, or was unable to execute\n"); } # Chop the off the end, do I trust the chpo function? # depends on the OS... $filesig =~ s/\n$//; return $filesig; } ############################################################################### # Main routine starts here. # ############################################################################### # Parse the command line for arguments and flags if($#ARGV==-1){&Help;} # help the user, they forgot the syntax, otherwise... if(($#ARGV==0) && (@ARGV[0] !~ /^-/)) { $Dir = shift(@ARGV); } else { foreach $arg (@ARGV) { if ($Verbose) { printf("debug: processing command line - arg %s \t ARGV[0] %s\n",$arg,$ARGV[0]); } if ($arg =~ /^-/) { if ($arg =~ /a/) { $Auto=1; } if ($arg =~ /c/) { $BaseLine=1; } if ($arg =~ /d/) { $DirCheck=1; } if ($arg =~ /f/) { $Config=1; } if ($arg =~ /g/) { $GnuHASHOut=1; } if ($arg =~ /i/) { $CreateDate=1; } if ($arg =~ /h/) { $UseHostName=1; } if ($arg =~ /l/) { $Logging=1; } if ($arg =~ /r/) { $Reporting=1; } if ($arg =~ /s/) { $Hash=1; } if ($arg =~ /v/) { $Verbose=1; } if ($arg =~ /x/) { $XTended=1; } } } shift(@ARGV); if ($Config) { $config = shift(@ARGV); } $Dir = shift(@ARGV); } # Give user syntax help if (($Dir eq "") && (!$Auto)) { &Help; } $Dir =~ s/\\/\//g; if ($BaseLine) { $DirCheck=1; } &Configure; # Make sure that you have a HASHFunc if you request the hash, otherwise the # open will try to execute the filename if($Hash) { if (!(-s $HASHFunc && -x $HASHFunc)) { &Error("Signaturing requested with -s switch, but no SIGNATURE program specified in config file"); } } # Allow for a higher security mode, where databases are written to one place # and read from another, with the same config file # Sanity check is if both are defined if (defined($ReadDB) && defined($WriteDB)){ #Only work with the automatic modes if (($BaseLine) && ($Auto)) { $DBFile = "$WriteDB"; } elsif ((!$BaseLine) && ($Auto)) { $DBFile = "$ReadDB"; } } #If a separate Authorization logger is not defined, try to approximate one if (($Logging) && (!$DOS) && (!defined($ALogger))) { #Push towards the auth log if this hasn't been defined #If you don't like it use the keyword and define it yourself $ALogger="${Logger}"; $ALoggerFlags="${LoggerFlags}"; foreach $atom (@LogFlags) { if ($atom =~ m/-p auth/) { $atom =~ s/[a-z0-7]*/auth/; } push(@ALogFlags,$atom); } } elsif (($Logging) && (!defined($ALogger))) { #Mimic normal logger $ALogger="${Logger}"; $ALoggerFlags="${LoggerFlags}"; (@ALogFlags)=split(' ',$ALoggerFlags); } if ($Verbose) { printf("debug: Processing host [%s]\n", $ThisHost); } # Handling only one database file now if ($BaseLine) { # Open for write open (DB, ">$DBFile") || &Error("no fcheck database exists! [$DBFile]"); printf(DB "# - Host %s\n",$ThisHost); printf(DB "# - OS %s\n",$ThisOS); $#junk = -1; (@junk) = stat("$DBFile"); $DBCTime = $junk[10]; $#junk = -1; printf(DB "# - Database Creation %s\n",&ctime($DBCTime)); if (!$DOS) { printf(DB "# - Uname %s\n",$Uname); } printf(DB "# - FCheck by Michael A. Gumienny\n"); } else { #Open for read open (DB, "<$DBFile") || &Error("no fcheck database exists! [$DBFile]"); #Suck the database into an array @Database=; close(DB); &BuildDBIndex } # Building a baseline is a significant event - syslog it if you can if (($Logging) && ($BaseLine)) { if(!$DOS) { # All rebuilds are significant $cmd=sprintf("\"INFO: Rebuild of the %s database %s begun for %s using config file %s\"\n",$Me,$DBFile,$ThisHost,$config); system($ALogger, @ALogFlags, $cmd); } } $Processing=""; if ($Auto) { if ($Reporting) { printf("Configuration: Configuration on %s begins with %s \n", $ThisHost,$config); for ($cti=0; $cti < $#ConfigTree + 1; $cti++) { printf("%s\n", $ConfigTree[$cti]); } printf("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"); } # Processing files and Directories except files are treated like one directory # Make sure we have files to process if ($#CheckFile != -1 ) { $Processing="Files"; if (!$BaseLine) { if (!$Logging) { printf("\nPROGRESS: validating integrity of Files\nSTATUS: "); } } if($Verbose) { printf("\nbuilding baseline for Files\n"); } if ((!&Scan_Build($Processing)) && (!$Logging)) { printf("passed...\n\n"); } undef(@BaseLineData); undef(@LiveData); } else #No FILE directives in config file { if ((!$BaseLine) && (!$Logging)) { printf("\nPROGRESS: No individual files to validating \n"); } if($Verbose) { printf("\nNo Files specified for baseline, check for directories\n"); } } # Make sure we have directories to process if ($#CheckDir != -1 ) { $Processing="Directory"; foreach $Dir (@CheckDir) { if (!$BaseLine) { if (!$Logging) { printf("\nPROGRESS: validating integrity of %s\nSTATUS:", $Dir); } } if($Verbose) { printf("\nbuilding baseline for %s\n", $Dir); } if ((!&Scan_Build($Processing)) && (!$Logging)) { printf("passed...\n\n"); } undef(@BaseLineData); undef(@LiveData); } printf(DB "# - - - - -> END Directories <- - - - -\n"); } else { # No DIRECTORY directives in config file if ((!$BaseLine) && (!$Logging)) { printf("\nPROGRESS: No directories specified in config file validation \n"); } if($Verbose) { printf("\nNo Directories specified for baseline\n"); } } if ($ReportFail != 0 ) { #Return 1 for not all passes exit(1); } } else #Not automatic mode { if (!$BaseLine) { if (!$Logging) { printf("\nPROGRESS: validating integrity of %s\nSTATUS: ", $Dir); } } else #Not automatic, but baselining (included from original fcheck) { if($Verbose) { printf("\nbuilding baseline for %s\n", $Dir); } if (-d $Dir ) { $Processing="Directory"; &Scan_Build($Processing); } elsif (-f $Dir ) { $Processing="Files"; &Scan_Build($Processing); } else { if (!$Logging) { printf("\nError: Command line argument: $Dir not a file or directory \n"); } &Help; } } } close(DB); if (!$DOS) { chmod 0600,$DBFile ; } fcheck/fcheck.cfg100444 0 0 10110 7253304741 12300 0ustar rootroot# FCheck.cfg (Sol) # # Directories to be monitored are shown below. Multiple entries may be used # by using the following 'keyword=variable' format: # # [Directory=(path/name)] # [Directory=(path/name)] # ... # # If you want recursive direcotry monitoring, place a / at the end of # the directory name, otherwise the script will interpret the entry as a # single file or single directory to monitor. # # For example the entry "Directory=/usr" # will watch everything in the /usr directory # # and the entry "Directory=/etc/passwd" # will monitor only the password file. # # while the entry "Directory=/usr/" # will watch everything in the /usr directory, and everything # recursively under it, (I.E. /usr/bin..., /usr/local/..., etc.) # Directory = /usr/local/admtools/ Directory = /tmp/ #Directory = C:/WINNT/ # WARNING # Use the following exclusions with care, # only include log files that are constantly undating and are known to # be written to frequently otherwise you can defeat the purpose of fcheck # by excluding too much... # # Specific files, and/or directories can be excluded. # # If used, configure them as full paths and their filenames. Directory # names must have a "/" appended to the end of its filename in the exclude # section. # #Exclusion = /tmp/dir/afile Exclusion = /usr/local/admtools/data/ #Exclusion = /usr/local/admtools/logs/ #Exclusion = C:/WINNT/TEMP/ # Miscellaneous settings are passed to fcheck from here. # # The baseline database files are to be kept under the "DataBase" directory # that is defined next. # DataBase = /usr/local/admtools/logs/sol.dbf #DataBase = C:/FCHECK/LOCALHOST.DBF # If you are using a read-only location. You can write the database files to # one location, and read from an alternate read-only (CD-ROM?) location. #ReadDB = /usr/local/data #WriteDB = /usr/local/data # Your systems interface for passing messages to its log files, UNIX systems # are typically found as "/usr/bin/logger". # # You could also send messages directly to a line printer if desired. # # Win32 platforms are forced to use line printers for now until a error # logging module is created for NT platforms. # #Logger = /usr/bin/lpr # # As of version 2.7.50, you pass logger taglines (-t) options through here. # Any other options can now be passed to third party loggers, scripts, etc. Logger = /usr/bin/logger -tfcheck #AuthLogger = /usr/bin/logger -tfcheck -pauth.info #AuthLogger = /usr/bin/logger -tfcheck -pauth.notice # This is the system command to determine a files type. Used to determine # pipes, major/minor numbers. # # Only useful on Unix platforms, not portable to Windows (yet). FileTyper = /bin/file # You may optionally set your hostname from the configuration file if FCheck # is unable to determine it on its own. # #HostName = "Mikes" # You may optionally set the system type from the configuration file if # FCheck is unable to determine it on its own. # Currently the only accepted option her is "System = DOS", otherwise FCheck # will default to a UNIX system. # #System = Dos # This must be set only for readability by you. It in no way effects the scan # function of FCheck. It only changes what is presented to the end user, so # the times that are presented to you may not be accurate if not set. TimeZone = EST5EDT # This is used only if you require/desire a hash signature to also be generated # for each file by use of the '-s' flag. If you do not use the (s)ignature # flag, then the following variable setting will not impact fcheck in any way. #$Signature = /usr/bin/sum #$Signature = /usr/bin/cksum #$Signature = /usr/bin/md5sum $Signature = /bin/cksum # Include an optional configuration file. # [CFInclude = (path/config_file_name)] #CFInclude # Used for individual file checking (I.E. FCheck databases!) # File = /usr/local/admtools/logs/sol.dbf # # End of FCheck.cfg file # fcheck/README100444 0 0 32416 7253307477 11302 0ustar rootroot FCheck: The filesystem baseline integrity checker. Copyright (C) 1996 Michael A. Gumienny Please send your comments, updates, improvements, wishes and bug reports for fcheck to: Michael A. Gumienny gumienny@hotmail.com ################################################################### This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA. Or you can find the full GNU GPL online at: http://www.gnu.org ################################################################### Files: Your distribution should contain the following seven (5) files: README Your reading this file. fcheck PERL script fcheck. fcheck.cfg Required configuration file. license GNU GPL License agreement. install Installation guide for all platforms. This documentation contains the following sections. Files: This sections you are reading now. Contains listing of files you should have included in your distribution. History behind FCheck: A brief introduction as to why FCheck was written. FCheck Features: What FCheck cna do for you. Changelog: Small, because FCheck was really written a few years ago but is now being added to. Operation: A brief intro to normal flag usage when you run FCheck. Closing Hints: A few tips from the author from real time usage experience. Mini FAQ: Questions that have filtered back to the author concerning operational problems. Complete detailed configuration and setup procedures can be found in the install.unix and install.win documents also included. ################################################################### History behind FCheck: Fcheck was developed out of necessity from a situation when my company outsourced its UNIX administrators. Originally intended for monitoring the administrators whimsical changes to the systems, it grew into a full-blown security tool. Being the person that went to the meetings and responsible for the systems (I.E. the guy with his head on the block), not knowing that a complete filesystem had been removed, happened only once. My "staff" had forgotten to notify me of the change, along with several other changes. I needed a way to monitor the system for any modifications and would report back to me immediately to stay abreast of whimsical changes. Thus, FCheck was born. FCheck grew into an overnight success, even though I did not see its complete potential at first. When a surprise Security Audit Team arrived, the full potential was recognized. Having several tools already in place to satisfy the auditors demands, they thought they had us when a baseline snapshot of the system was requested. Expecting to hear that we had no such tool in place, they were eager to learn more about FCheck and its capabilities. ################################################################### FCheck Features: Essentially, FCheck has the ability to monitor directories, files or complete filesystems for any additions, deletions, and modifications. It is configurable to exclude active log files, and can be ran as often as needed from the command line or cron making it extremely difficult to circumvent. It is written in standard PERL and requires no special outside library modules. Currently there are a few 'Tripwire' style baseline system security tools and most are purchasable with licensing agreements, etc. Personally I hate software that you must purchase so this is distributed under the GNU license. (I.E. It's yours to play with, but keep my name in it, and let me know what you modified so that others can share the benefits). FCheck was further developed with the junior administrator in mind that do not yet understand the complex configuration files and operation required to run many security products. All code is written from scratch, and is owned solely by the author, but rights are granted for its usage under the GNU license agreement to any site that desires free baseline security measures. ################################################################### Changelog: See the script, it's getting big! Major Updates Provided in this release: o Added ability to determine version of MD5 being used. o Modified the routines that call MD5 and "file" to pipes, slight speed increase and less vulnerable to shell exploits. Update in last release: o Databases merged into one database, DATABASE= configuration keyword now points to the full path and filename to use for that database. o Added the "-h" option to look for the configuration file with the $HOSTNAME environment variable appended to the end fo it. (This is useful in distributed system environments.) (Example) $HOSTNAME=myhost fcheck -ahf A_Config.dbf Result: fcheck would use a configuration file of "A_Config.dbf.myhost" o Added the "-r" option to create a report suitable for email. The generated report will show good, and bad integrity checks. o Added the "-x" option to allow monitoring the "number of links", "UID", "GID", and the "Major/Minor" numbers of device files. o Added the "FILE=" keyword in the configuration file. This will allow you to monitor single files, rather than entire directory contents. o Added the "FILETYPER=" keyword in the configuration file. This needs to be set if you use the "-x" option, and is what will allow you to determine file types, and major/minor numbers of device files. ################################################################### Operation, and Getting Started: Flag passing is a fairly simple process. Primarily you will be using two commands. One builds (or rebuilds) your baseline database files (system snapshots). The second runs in a scanning comparison mode. "fcheck -ac" Builds the baseline database. "fcheck -a" Comparison scans the system against the baseline database. For normal operation: Initially you will run fcheck by issuing the command "fcheck -ac" to create the initial baseline file used for comparison. Any runs after the creation of the basline will normally be with the flags "fcheck -a" to scan for any system modifications. After a scan is completed, you will probably want to have fcheck re-create its baseline database for the next comparison cycle. Otherwise you will be seeing every system modification since the last baseline re-build. In other words, run the "fcheck -ac" command again. (Advanced Note:) A more intensive system check would be accomplished by building your database to include GID/UID checks, directories, and CRC checks by using the following sample syntax: "fcheck -cadsxlhf /usr/local/admtools/etc/fcheck.dbf.yourhost" And provide periodic integrity scans from cron by using the following sample syntax: "fcheck -adsxlhf /usr/local/admtools/etc/fcheck.dbf.yourhost" ################################################################### Closing Hints: I would also suggest using the "l" flag to send messages to syslog unless you really want to watch the output from this all the time. You could also make use of some log monitoring packages like CA-Unicenter, HP-Openview, or several other shareware alternatives including 'xlog' or even the 'pmem' Tcl/Tk interface that I also wrote. FCheck was ran from cron in a production environment at 10 minute intervals with no impact to system performance. Message logging was handled by syslog with the "-l" flag and imported to a commercial event monitoring package that monitored and displayed system logfiles, highlighting only the important events. A shorter duration can be obtained on smaller systems, but you must allow FCheck to complete its baseline comparison before re-building the baseline to alleviate false readings. Actual interval times will vary depending on how active a system you are running FCheck on. Those of you that have scanned the early code may have noticed the remote shell feature has been removed. I felt this offered too much temptation to open a security hole and was removed. Fcheck does NOT have to run as root, but it does need to have read permissions to each of the directories and files that you want to monitor. Other flags for you to play with are as follows: -a Automatic mode, do all directories in configuration file. -c Create a new base line database for the given directory. -d Directory names are to be monitored for changes also. -f Use alternate 'filename' as the configuration file. -i Ignore creation times, check permissions, adds, deletes only. -h Append the $HOSTNAME to the configuration filename. -l Log information to logger rather than stdout messages. -r Report mode, great for emailed status reports. -s Sign each file with a CRC/hash signature. -v Verbose mode, not used for report generation. -x eXtended unix checks, # of links, UID, GID, Major/Minor checks. Final Notes: As stated elsewhere in this README. If you have suggestions please forward them to me and I'll try to accommodate them. If they make sense and others have requested the same changes, then they may make it into the next release. * THREATS ARE IGNORED WHEN YOUR SUGGESTION DOES NOT GET WRITTEN INTO A RELEASE * This is free software and I don't make a living from it. It is also distributed under the terms of the GNU General Public License WITHOUT WARRANTY! ################################################################### Mini FAQ: Q: When I try to initialize with the command "FCheck -ac" I get the following error message back. Why? FCheck: Can't find C:/Work/temp/perl/fcheck/FCheck.cfg terminating... A: FCheck can't locate the configuration file that you have instructed it to use. Edit the executable (FCheck) and ensure that the variable "$config=" is set properly to reflect your configuration files location. Q: When I try to initialize with the command "FCheck -ac" I get the following error message back. Why? FCheck: no base file directory exist! [C:/Work/temp/perl/fcheck/data] terminating... A: The directory that you have instructed FCheck to utilize to store its database does not exist. Either modify the configuration file (FCheck.cfg) to use an existing directory, or create the one it needs. Q: I have removed a directory "/usr/local/etc" and told FCheck to exclude it from future scans with the line "Exclusion = /usr/local/etc/", now it is being reported as deleted. A: But, the scanned directory does still exist in FChecks databases. After a modification to any scanned area of a system. You must tell FCheck to re-initialize its database (FCheck -ac) to stop this behaviour. Otherwise FCheck will continue to report any changes that it has detected, including the directory you told it to exclude from future scans. Once you have re-initialize the databases, only then will FCheck ignore any directories or files that you instructed it to exclude. Q: FCheck says "debug: (GetDir) No can do (/some_file)..." when I try to monitor a file. Does "Directory =" have to be a Directory for File Name? A: Okay, you caught me! FCheck never had any real documentation until recently which means there is bound to be an error or two. Some more noticeable than others. You must use the directories name that you wish to monitor. As an option, you can monitor that directory recursively by placing a "/" at the end of the path ("/etc" for the immediate directory, or "/etc/" for recursive). For you to monitor only your "/etc/passwd" you would have an entry of "Directory = /etc" and then you would use several excludes such as "Exclude = /etc/group", "Exclude = /etc/motd", and so on. But I think that you will probably want to monitor the entire "/etc" directory for changes. Q: Gzip says "decompression OK, trailing garbage ignored." When I uncompress FCheck, is my tar file damaged? A: The Netscape WEB site appears to be padding GZipped files with NULLS, although it does not happen to the identical Pkzipped file. As expressed in the warning message, GZip ignores the trailing NULL characters with no impact to the extracted tar file. If the displayed warning bothers you too much, then try the Pkzipped version of FCheck as it is an identical varsion. fcheck/install100444 0 0 31111 7253304725 11773 0ustar rootroot FCheck: The filesystem baseline integrity checker. Copyright (C) 1996 Michael A. Gumienny Please send your comments, updates, improvements, wishes and bug reports for fcheck to: Michael A. Gumienny gumienny@hotmail.com ################################################################### Note: FCheck operation is essentially identical for both UNIX and Win32 platforms, the only difference is the inclusion of a drive letter. (DOS Note) Please note that all DOS platform path names are entered with a UNIX style forward slash "/", I.E. "C:/", "D:/games/warped", and "C:/Windows". Any number of drives may be included, and drive letters can be omitted for any actions on the drive that FCheck is installed on, (I.E. "/Windows", and "/Windows/system" can be used without a drive letter pre-pended to the path). Requirements: ============= FCheck was initially developed for HP/UX and Sun Solaris systems running PERL 5.x. It should run on any platform that supports PERL scripts with no difficulties. FCheck has been successfully tested and operated by the author on the following systems: AIX BSD and variants (BSDi, NetBSD, FreeBSD, OpenBSD) HP/UX Linux SCO Solaris SunOS Windows 95/98/NT Win3.x (See note) Note: It is possible to operate versions of FCheck prior to 2.07.45 on 16 bit DOS platforms by carefull selection of directory names and slight modification to the script. Contact the author if a more detailed explanation is desired. ################################################################### Installing FCheck: ================== 1. Read the README and Release Notes for the latest FCheck information. FCheck is distributed as a tarred and gzip'ed file for UNIX systems. The same version is also available as a Zipped archive for any DOS platforms that can not uncompress the UNIX version. Because FCheck can be ran on any platform that supports PERL and long filenames (DOS or UNIX). No slick installation scripts are included. 2. Decide what directories you are going to install FCheck in and where to keep its database files. The most commonly used UNIX directories are "/usr/local/fcheck" for the executable and configuration files, and "/usr/local/fcheck/fcheck.dbf" for the database storage area. (DOS Note) You could use a directory called "C:/FCheck" for the executable and configuration files, and "C:/FCheck/fcheck.dbf" for the database storage area on DOS based platforms, but anything is acceptable. 3. Copy the FCheck executable (FCheck) and configuration file (FCheck.cfg) from your downloaded distribution to the location selected in the last item (2 above) to the machine on which you wish to install and run the product. 4. Modify the FCheck executable to reflect appropriate paths selected for your system. Find the "User modifiable variable definitions" section (shown below) and modify it to reflect your site requirements as needed by the following: a) Ensure the variable ($config="/usr/local/etc/fcheck.cfg" or $config="C:/FCheck/FCheck.cfg") depending on your platform is set properly in the executable (FCheck) if you have renamed your configuration file or install path to anything other than the included default FCheck.cfg. ########################################################################## # # # User modifiable variable definitions: # # # ########################################################################## $config="/usr/local/etc/fcheck.conf"; # If you have a Win32 system, include the Drive letter like this example $config="C:/FCheck/FCheck.cfg" b) Note: This can be over-ridden by passing the '-f' flag and the complete path and name of an alternate configuration file. ################################################################### Configure FCheck: ================= 5. Read the included README for an overview of FCheck fundamentals. The configuration file is comprised of only seven reserved keywords that are used to pass information to FCheck. Those keywords are: - Directory - Exclusion - DataBase - Logger - Hostname - System - TimeZone These keyword definitions can be used multiple times within the configuration file to pass your definitions as follows: Keyword = Your definition of this variable Keyword Definitions: ==================== Directory: ---------- Used to define all directories that are to be monitored by FCheck. To monitor the "/etc" directory, the line is entered as follows: Directory = /etc # Win32 systems would include a like as follows: Directory = C:/etc To tell FCheck to monitor directories recursively (I.E. /etc and all directories below it, excluding symbolic links) is accomplished by appending a "/" to the end of a definition. Directory = /etc/ # Win32 systems include the drive letter as follows: Directory = C:/etc/ The preceding entry would monitor the "/etc/" directories recursively. The one exception to this recursive rule is a root directory entry. Directory = / # Win32 systems include the drive letter as follows: Directory = C:/ This entry will NOT return recursive results, but will check only the defined top-level root directory. Individual subdirectories will have to be defined separately with multiple "Directory =" entries. This is a residual effect of clean UNIX systems not utilizing their top level directory for anything other than the kernel. Exclusion: ---------- Used to define files or directories to pass over when verifying integrity. If your system contains active log files that are updated frequently (who doesn't). You would need to use the "Exclusion =" definition to prevent FCheck from monitoring that file or directory tree. Exclusion = /var/adm/ # Win32 systems include the drive letter as follows: Exclusion = C:/Windows/Temporary Internet Files/ The above example would pass over all of the actively updated log files on a typical UNIX (or DOS) system. Note that the appended "/" IS required should the excluded file be a directory name. Otherwise FCheck will attempt to interpret the exclusion as a filename and not a directory. File: --------- Used to convey the full path and filename that FCheck should utilize DataBase: --------- Used to convey the full path and filename that FCheck should utilize to store all of its baseline generated files. As pointed out elsewhere in this document the database filename that you use could be any existing directory and filename of your own desire. Database = /usr/local/fcheck/fcheck.dbf # Win32 systems include the drive letter as follows: Database = C:/FCheck/FCheck.dbf The above example would utilize the "/usr/local/fcheck/db" directory for storage of baseline snapshot databases, and "C:/Fcheck/FCheck.dbf" on a DOS system. Logger: ------- Used to convey the executable file that should be used to transmit messages to your particular systems log files. It is activated by use of the "-l" flag. Logger = /usr/bin/logger The Logger keyword could also be used to send messages to an attached printer if log files are not readily available by use of the following example syntax: Logger = /usr/bin/lpr Hostname: --------- This keyword is used to pass the systems hostname should FCheck not be able to determine the hostname automatically. This will be a common issue on Windows 95/98 platforms without a 'hostname' function, but should not hinder UNIX operation. You should use the environment variable 'HOSTNAME=' within UNIX but if you cannot do this, then the hostname can be set by use of this keyword. System: ------- By default, FCheck is designed to assume that it is operating on a UNIX platform. On some rare occasions it is possible that FCheck may become confused and assume that it is on a DOS platform. If this situation happens to you, then you can use the System keyword like this example: System = UNIX Currently FCheck only runs on UNIX by default and 32bit DOS platforms that support long filenames and PERL. So "DOS" is the only valid entry that can be used for this keywords definition. Any other entry will force FCheck to assume it is operating on a UNIX platform. TimeZone: --------- This is an overide of the environment variable TZ. It in no way effects the operation of FCheck other than how time is presented to the end user. TimeZone = EST5EDT Signature: ---------- This is used only if you require/desire a hash signature to also be generated for each of the files by use of the '-s' flag. This does not allow for the granularity of individual file selection as Tripwire, but allows operation to remain easier. You may select any CRC/hash signature function that you feel secure and comfortable with that is installed on your system by including the 'Signature' keyword along with the intended functions location in the configuration file as follows: Signature = /usr/bin/md5sum As previously stated, you should be able to use any CRC/hash function with FCheck including but not limited to, 'sum', 'cksum', and 'md5sum' to name only a few of the more common functions available. Note: Simple CRC calculations can produce identical results for files that are NOT identical! Use caution and common sense on your own CRC or hash selection for your reliabilty factor to remain high. 6. Modify the default configuration file (FCheck.cfg) that has been provided only as a bare bones sample starting point. Or, create your own based on your own particular needs from what you have learned in section five above. ################################################################### Running FCheck ============== (The next steps assume you are running from FCheck's directory that you installed to. Change the paths and filenames as appropriate for you.) 7. Initiate FChecks database by using the (-c)reate and (-a)ll flags as follows: ./FCheck -ca You can include the -(v)erbose flag if you would like to see the progress of FCheck. 8. Set up FCheck to scan your system for any modifications made since the last baseline snapshot (FCheck -ca) that you have. The best method of operation is through an unattended crontab entry with the least amount of time possible between scans. You can also run FCheck interactively from the command line or cron by use of the following (-a)ll flag example: ./FCheck -a Congratulations! If you successfully completed these steps, then FCheck is set and ready to go. ################################################################### Conclusions: ============ FCheck can be ran with very little time required between runs, dependent only upon the amount of monitored resources and your CPUs speed, making it very difficult to circumvent. Once a change has been detected, you may wish to reinitialize the baseline database (fcheck -ac) to prevent FCheck from reporting the change again. If you are logging to a printer, this is a very good idea to save paper! If you are using NT/Win2000, then try running FCheck with the "at" command use any shareware "cron" style command if "at" is not available.