#!/bin/bash # ============================================================================== # # detectChanges version 0.5 written by Abi Arroyo # Visit for updates # # This is subject to the GNU GPL version 2 released June 1991. # # Introduction # ============================================================================== # # This script is a file integrity checking and management tool. Several # applications exist in this product space, but none of them are written # in a scripting language and as a result require package installation or # compilation. This can cause some problems on systems where a compiler is # not available or where administrative privileges have not been granted. # While detectChanges does not provide as much functionality, it is sufficient # for most users; and provides a simple command line interface. # # Application Notes # ============================================================================== # # (1) # Sometimes when the file is transfered between a Windows and Unix systems # errors are encountered when trying to run the script. Sample errors are # shown below: # # detectChanges.sh: line 2: $'\r': command not found # detectChanges.sh: line 24: $'\r': command not found # detectChanges.sh: line 25: $'\r': command not found # detectChanges.sh: line 27: $'\r': command not found # detectChanges.sh: line 28: syntax error near unexpected token `$'{\r'' # detectChanges.sh: line 28: `createMapping () { # # To fix this error open the script in vi and to convert the file to a # Unix format by typing the following exactly as shown ":set ff=unix" # # (2) # The storage directory ($dcStorage) may contain temporary storage files # (*_diff.int) if the debug switch is enabled or if the application # is stopped prematurely. These files may be safely deleted. # # (3) # It may also be necessary to configure some variables listed below in the # "user configurable variables" section. The only other requirement for # running the script is having the following software available: # # * Bourne Shell # * OpenSSL # * find # * grep # * diff # * readlink # # (4) # The script has been tested on the following operating systems but should # be supported on any OS that has the software listed above in section (3). # # CYGWIN_NT-5.1 DTC1200F32D2F1E 1.5.24(0.156/4/2) 2007-01-31 10:57 i686 Cygwin # # I want to expand the list of supported operating systems. If you # successfully run this script I would appreciate if you sent me an e-mail # with the output of `uname -a` for your system. Please submit an e-mail # via the following web interface . # # (5) # Current the application is able to detect changes based on the md5 and sha1 # digest algorithms, in addition to file permissions and user/group ownership. # More checks can be added with ease. The following comment has been added to # aide modifications: # ADD HERE FOR MORE CHECKS # # Todo # ============================================================================== # # * Add support for encrypting checksum files # * checksum support for integrity file (intFile) and check file (chkFile) # # Change log # ============================================================================== # # 2007 08 21 # - cleaned up code # - clarified variable names # - more restrictive persmissions on script storage # - changed checksum utility from md5 to openssl # - improved documentation (description and fixing file format error) # 2007 08 28 # - removed createMapping() function and developed better method # - added debug mode # - fixed bug detecting file modifications (moved from grep to diff) # 2007 09 01 # - fixed bug detecting numChanges # 2007 09 17 # - updated modification detection mechanism # - updated reporting mechanism # - updated web site form # 2007 09 22 # - removed extra printing for number of changes that have been detected # - fixed permissions error (found by Mikhail Ru) # - added todo list # # ============================================================================== # user configurable variables ----------------------------------------------------- # # retrieve the name of the shell script # cmd="detectChanges" # # set the directory where checksums and configuration files are stored # dcStorage="./dcStorage" # # location of openssl # openssl="/bin/openssl" # functions ------------------------------------------------------------------------ # # convert relative path (../asdf/jkl/) to absolute path. also removes # trailing directory path delimeter with usage of pwd # absPath () { tmp_file=`readlink -f $1` if [ -d "$tmp_file" ]; then tmp_file="${tmp_file}/" fi echo "$tmp_file" } usage () { echo -e "\n\t$cmd [ -init | -diff ] directory ... directoryN -exclude dirA -exclude dirB" echo -e "\n\t\t-help\t\tprint this screen" echo -e "\t\t-init\t\tinitialize checksums" echo -e "\t\t-diff\t\tdetect checksums changes (must run -init first)" echo -e "\t\t-exclude\texclude these directories (must be included with init and diff)" echo -e "\t\t-verbose\tshow differences in files instead of only listing filename changes" echo -e "\t\t-debug\t\tenable debug mode (most verbose, does not delete storage files)" } error () { usage; echo -e "\n\terror: ${*}" exit -1 } # internal variables ---------------------------------------------------------------- # # time # theTime=`date +%Y%m%d_%H%M%S` # # report # report="${dcStorage}/${theTime}_fsChangeReport.txt" # # delimeter # delim=":" # process command line arguments ----------------------------------------------- if [ -z "$1" ]; then error "command line parameters not specified" fi while [ "$1" != "" ]; do case $1 in -init ) shift; init=true ;; -verbose ) shift; verbose=true ;; -diff ) shift; diff=true ;; -debug ) shift; debug=true ;; -dir ) shift; dir="$1"; shift ;; -exclude ) # determine if file is a symbolic link # if so, exclude symbolic link and actual directory shift path="`readlink -f $1`" if [ -z "$exclude" ]; then exclude="-wholename \"${path}\" -prune"; else exclude="${exclude} -o -wholename \"${path}\" -prune" fi shift ;; -v ) shift; verbose=true; shift ;; -h | -help | --help ) usage; exit 1;; * ) if [ ! -e $1 ]; then error "unknown parameter: $1" fi input_path="$1 $input_path"; shift ;; esac done if [ ! -z "${exclude}" ]; then exclude="${exclude} -o" fi # error checking --------------------------------------------------------------- if [ -z "$input_path" ]; then error "directory not specified" fi if [ ! -e $dcStorage ]; then mkdir -p $dcStorage if [ ! -e $dcStorage ]; then error "unable to create script storage directory: $dcStorage" fi fi chmod u+rwx,og-rwx "${dcStorage}" if [ -z $init ]; then if [ -z $diff ]; then error "initialization or comparison have not selected" fi fi if [ ! -z $init ]; then if [ ! -z $diff ]; then error "cannot initialize and compare at the same time (-init or -diff, not both)" fi fi # # check to see if openssl is installed # if [ -x $openssl ]; then $openssl list-message-digest-commands | grep -E -v "md5|sha1" > /dev/null if [ $? -ne 0 ]; then error "unable to locate ssl: set openssl variable in script: $openssl" fi else error "unable to locate ssl: set openssl variable in script: $openssl" fi # generate data and perform comparison if selected ----------------------------- for i in `echo $input_path`; do # determine absolute path of file entry="`absPath $i`" intFile="$dcStorage/`echo "$entry" | tr -d /`.int" # file containing the initialized data chkFile="${dcStorage}/${theTime}_diff.int" # temporary data storage #echo "storage directory: $dcStorage" #echo "file with integrity data: $intFile" #echo "comparison check file: $chkFile" #echo "exclude expr: $exclude" #echo "processing: $entry" if [ ! -e "$intFile" ]; then if [ ! -z $diff ]; then error "unable to compare checksums since they have not been created: $entry" fi fi OFS="${IFS}" IFS=$'\n' rm -rf $chkFile echo "" for j in `eval find \"$entry\" $exclude -print`; do echo -n "." # ADD HERE FOR MORE CHECKS if [ -d "$j" ]; then # directories perms=`ls -ald "$j" | awk '{ print $1 $3 $4 }'` echo "${perms}${delim}${j}" >> $chkFile else # files md5=`$openssl md5 "$j" | sed -e s/.*\=\ //g` sha1=`$openssl sha1 "$j" | sed -e s/.*\=\ //g` perms=`ls -al "$j" | awk '{ print $1 $3 $4 }'` echo "${md5}${delim}${sha1}${delim}${perms}${delim}${j}" >> $chkFile fi done IFS="${OFS}" if [ ! -z $diff ]; then if [ ! -z $verbose ] || [ ! -z $debug ]; then echo -e "\n\ndiff ${intFile} ${chkFile}" diff "$intFile" "$chkFile" fi numChanges=`diff "$intFile" "$chkFile" | grep ${delim} | sed -e s/.*\\${delim}//g | uniq | wc -l | awk '{ print $1 }'` if [ $numChanges -gt 1 ]; then echo -e "\n\nthe following file(s) have been modified\n" diff "$intFile" "$chkFile" | grep ${delim} | sed -e s/.*\\${delim}/\ \ \ /g | uniq | grep -v "$intFile" > "$report" # # This note explains why the "grep -v" statement is listed above: # # The mechanism used for detecting changes is that you create a # list of checksums and then put them into a file. However, it is not # possible to validate the checksum file containing the list of all of # the checksums. # # When you compute the checksum for the file and then attempt to add the # file to itself, then the checksum changes. # # One solution is to place the checksum in another file and then perform # a comparison on the file and the new checksum comparison file that is # created. # cat "$report" chmod u+r,u-wx,og-rwx "$report" echo "" else echo -e "\n\nno changes have been found\n" fi else mv "$chkFile" "$intFile" echo -e "\n\nfile integrity information saved to: $intFile" chmod u+r,u-wx,og-rwx "$intFile" fi if [ -z $debug ]; then rm -rf "$chkFile" else chmod u+r,u-wx,og-rwx "$chkFile" fi done