#! /bin/bash # /root/bin/mktripwire.sh \ # /etc/tripwire/mktripwire.sh ] Pick a location # /usr/local/sbin/mktripwire.sh / # A Gentoo-oriented Tripwire Policy Generator # This script outputs tripwire policy against a Gentoo configuration # Processing Gentoo packages with this script depends on qlist (from portage-utils) # This script can be adapted to other package managers; e.g., rpm, pacman, dpkg # This script checks whether or not a listed package or file exists # This script probably only runs under a bash shell # As of 21 Nov 2010, this script is a work in progress # c.cboldt at gmail.com # 0.0.1 # 101106 Sent to http://bugs.gentoo.org/34662 # 0.0.2 # 101107 Sent to http://bugs.gentoo.org/344577 # Added invocation parameters, help and version messages # Added option to take RULENAME[], etc. from separate config file # Added SEC_MOD variable array # Added FILELIST_x[], REMARKS_x[], and SEC_MOD_x[] arrays # Replaced XWINLIST[] and SKIPINOD[] arrays with FILELIST_x[] arrays # Added options for terse and verbose progress reporting # Added "hidden" debug mode (the word "debug" followed by optional rulenumber) # Added default RULENAMEs for Database (e.g., mysql) and Programming # Added default RULENAME using /var/log/*[g] filelist # Added warning: default generated policy does not check /var/log directory VERSION=0.0.3 # 101121 Sent to http://bugs.gentoo.org/344577 # Substituted `qlist --exact` for `equery --quiet files` (significantly faster) # Changed variable name REMARKS[] to COMMENTS[] # Moved credits to copyright message in footer # Fixed bug: some variables were not cleared for external config # Fixed bug: SGID attribute of $targetfile was ignored # Added RECURSE_x[] array # Applied SEC_MOD[] variable to files that are directory names # Eliminated "exit" when qlist/equery not found (script processes any file list) # Added option to delete text policy file after tripwire --update-policy # Renamed some routines to better represent their functions # Added numerous PACKAGES, FILELIST entries, renamed several RULENAMES # Made numerous policy level changes, moving of files between rules, etc. # Added FILELIST[]="/lib/rcscripts/*/*" entry to "Gentoo Specific Files" rule # Added tripwire policy mask reference information to header # Added system variables (TWPOL, TWKEY, TWDB, TWREPORT) for portability # Uses tripwire internal aliases instead of SEC_LOG, SEC_TTY, SEC_CONFIG, etc. # Removed SIG_LOW, SIG_MED, and SIG_HI variables; now uses numerical values # Substituted `twadmin --create-policy` for `tripwire --update-policy` # Substituted `tripwire --init` for `tripwire --check --interactive` # Added facilties for future adaptation to other package managers; see: # http://www.syntaxtechnology.com/2009/07/install-tripwire-on-fedora-11 # ============================================================================== # Improvements? rationalize categories and security level settings # confirm operation of emailto function # more complete/useful packages lists # break printing of long package or file lists into multiple lines # better help messages and comments # - tmpwatch on /var/lib/tripwire/report # better error and condition checking # - needs bash, maybe some grep issues # maybe more ignorelists # maybe automate copying of text and key files to removable media # maybe automate creation of available package lists # (but categorization here is radically different from Gentoo's "categories") # ============================================================================== # System variables to adapt script to tripwire installation locations # All variables can be set by a separate configfile # Why would one bother to use independent $TWSKEY and $TWLKEY directories? TWPOL=/etc/tripwire # Directory containing policy TWKEY=$TWPOL # Directory containing encryption keys TWDB=/var/lib/tripwire # Directory containing database TWREPORT=/var/lib/tripwire/report # Directory containing reports # Package query variables for future adapting of script to package query programs # See process_packagename routine, now tested ONLY for Gentoo # on mobile AMD Athlon(tm) 4 Processor @ 900 MHz: # equery -> real 9m 6.620s # qlist -> real 1m 43.399s # QUERY_DISTRO="equery --quiet files" # Gentoo - app-portage/gentoolkit QUERY_DISTRO="qlist --exact" # Gentoo - app-portage/portage-utils # QUERY_DISTRO="pacman --quiet --list" # Arch Linux # QUERY_DISTRO="dpkg --listfiles" # Debian # QUERY_DISTRO="cave contents" # Paludis # QUERY_DISTRO="rpm -ql" # Red Hat, OpenSuSE, others # QUERY_DISTRO="pkg_info --quiet -L" # FreeBSD QUERY_PACKAGE="" # Unused variable, for now # TMP_FILE contains a list of files associated with an installed package TMP_FILE=/tmp/mktripwire.tmp # RULENAME[] Rule Name, unique to avoid errors by tripwire --update-policy # PACKAGES[] Optional list of Gentoo package names under this Rule Name # IGNORLST[] Optional list of files to ignore under this Rule Name # FILELIST[] Optional lists of individual file names (wildcards okay) # COMMENTS[] Optional comments associated with individual FileLists # EMAILTO[] defaults to empty - written rule-by-rule # SEVERITY[] defaults to 100 - written rule-by-rule # # BINSECVALUE[] defaults to $(ReadOnly) # ETCSECVALUE[] defaults to $(Dynamic) - applies to Directories # LOGSECVALUE[] defaults to $(Growing) # RECURSE[] defaults to (recurse=true) # SEC_MOD[] defaults to empty - written file-by-file ##### Start of Package and File Lists ##### Package Lists and Multiple File Lists may be combined under one rule ##### See Root User Directory Rule for multiple filelists example RULENAME[0]='System Auditing Programs' PACKAGES[0]='tripwire aide chkrootkit lynis nagios openscap osiris rkhunter yasat' EMAILTO[0]='"root@localhost"' BINSECVALUE[0]=ReadOnly RULENAME[1]='Invariant Directories' COMMENTS[1]='Owner and group should remain static' FILELIST[1]='/ /home /etc /mnt /opt' SEVERITY[1]='60' ETCSECVALUE[1]='Invariant' RECURSE[1]=' (recurse = 0)' RULENAME[2]='Temporary Directories' FILELIST[2]='/usr/tmp /var/tmp /tmp' SEVERITY[2]='35' ETCSECVALUE[2]='Invariant' RECURSE[2]=' (recurse = 0)' RULENAME[3]='[core|diff|find]utils procps' PACKAGES[3]='coreutils diffutils findutils procps htop lsof' # Notice disambiguation by including category with a package name RULENAME[4]='Compression/Archiving Programs' PACKAGES[4]="tar star bzip2 gzip zip unzip xz-utils app-arch/lzma" # Todo: improve rulenames and package split re: Networking RULENAME[5]='Network Setup Programs' PACKAGES[5]='net-tools iproute2 iputils iptables mgetty dhcpcd ppp' RULENAME[6]='Networking Programs' PACKAGES[6]="tcpdump tcp-wrappers rsync samba distcc dnsmasq bind bind-tools \ knock telnet-bsd netcat wireshark nmap fail2ban denyhosts mrtg" ETCSECVALUE[6]=ReadOnly RULENAME[7]='Hardware and Device Programs' PACKAGES[7]="udev pciutils util-linux sysvinit psmisc kbd hdparm smartmontools \ lshw ethtool hotplug-base module-init-tools setserial dmraid" COMMENTS[7]='udev device creation policies and scripts' FILELIST[7]='/etc/udev/rules.d /etc/udev/scripts' # progsreiserfs produces no bin/ or /etc/ files ... # it produces /usr/lib/libdal.so and /usr/lib/libreiserfs.so RULENAME[8]='Filesystem Programs' PACKAGES[8]="e2fsprogs reiserfsprogs reiser4progs xfs nfs jfs \ pax-utils sysfsutils autofs lvm2 mdadm fuse sshfs-fuse" COMMENTS[8]='Files produced by progsreiserfs' FILELIST[8]='/usr/lib/libdal.* /usr/lib/libreiserfs.*' RULENAME[9]='File Manipulation Programs' PACKAGES[9]="gawk grep patch cpio file gettext groff less man mlocate \ ncurses slang sed slocate patchutils debianutils" RULENAME[10]='Toolchain Programs' PACKAGES[10]='gcc binutils glibc libtool make autoconf automake' RULENAME[11]='Security Related Programs' PACKAGES[11]='shadow pam openssl openssh gnupg' RULENAME[12]='Database Related Programs' PACKAGES[12]='mysql postgresql-server sqlite' RULENAME[13]='Programming Languages' PACKAGES[13]='perl php python ruby swig tcl tk' RULENAME[14]='MTA Related Programs' PACKAGES[14]='sendmail postfix ssmtp mailx procmail dovecot clamav spamassassin' RULENAME[15]='P2P Related Programs' PACKAGES[15]='ejabberd jabberd jabberd2 mu-conference' RULENAME[16]='WWW Related Programs' PACKAGES[16]='apache bozohttpd lighttpd mini_httpd thttpd' RULENAME[17]='Shell Programs' PACKAGES[17]='bash zsh csh tcsh sash rssh busybox screen' BINSECVALUE[17]=ReadOnly RULENAME[18]='Editor Programs' PACKAGES[18]='nano joe vim ed emacs' COMMENTS[18]='Shared config files' FILELIST[18]='/usr/share/nano' # Detect new and removed user crontabs. Ignore modification of existing crontabs. RULENAME[19]='Cron, Inetd, and Logging' PACKAGES[19]="anacron bcron cronie dcron fcron incron vixie-cron xinetd \ newsyslog rsyslog syslog-ng logrotate" COMMENTS[19]='User-installed crontabs' FILELIST[19]='/var/spool/cron/crontabs' RULENAME[20]='Boot Selector Programs' PACKAGES[20]='grub lilo' COMMENTS[20]='Contents of /boot directory are safer on an unmounted partition' FILELIST[20]='/boot/* /lib/modules' RULENAME[21]='Package Manager Programs' PACKAGES[21]='pacman paludis rpm' # Package rule ignores scripts installed to /lib/rcscripts # FILELIST[] checks for files installed to /lib/rcscripts # Note SGID attribute of /usr/local/portage directory RULENAME[22]='Gentoo Specific Programs' PACKAGES[22]='portage portage-utils gentoolkit baselayout eix' COMMENTS[22]='Gentoo-installed scripts' FILELIST[22]='/lib/rcscripts/*/*' COMMENTS_2[22]='Local ebuilds - strict policy applied to SGID directory' FILELIST_2[22]='/usr/local/portage' ##### End of Gentoo package lists ##### ##### Some File Lists cribbed from RedHat policy file # Some local config files can be found with this code snippet # for i in `locate -r etc.*[lL]ocal` # do [ -z "`qfile $i`" ] && echo $i # done # # find / -group kmem -perm -2000 -print # Finds SGID files, owned by kmem # find / -user root -perm -4000 -print # Finds SUID files, owned by root RULENAME[23]='Local Config Files' FILELIST[23]="/etc/bash/bashrc.local \ /etc/dovecot/dovecot-local.conf \ /etc/dnsmasq-local.conf \ /etc/env.d/00Local \ /etc/host-local-block \ /etc/host-banner-ads \ /etc/hosts \ /etc/hosts.allow \ /etc/hosts.deny \ /etc/lilo.conf \ /etc/lynx/lynx-site.cfg \ /etc/ppp/chap-secrets \ /etc/ppp/ip-up.d/00-local.sh \ /etc/ppp/ip-down.d/00-local.sh \ /etc/rkhunter.conf.local \ /etc/screenrc-local \ /etc/syslog-ng/syslog-local.conf \ /etc/udev/rules.d/10-local.rules" # Policy generator avoids opening devices (recursion) by applying # the $(Device) policy for block and character special devices # See "select_policy" routine # Inode changes for nearly all of these files, under creation by udev RULENAME[24]='Critical Devices' COMMENTS[24]='Red Hat example config noted kmem, mem, null, zero' FILELIST[24]="/dev/kmem /dev/mem /dev/null /dev/zero \ /dev/log /dev/cua0 /dev/console \ /dev/tty[123456789] /dev/tty1[012] \ /dev/urandom /dev/initctl /proc/*" SEC_MOD[24]=' -i' # ETCSECVALUE[] applies to files that `stat` as directory names RULENAME[25]='OS Bin and Lib Directories' FILELIST[25]='/bin /sbin /lib' ETCSECVALUE[25]=ReadOnly FILELIST_2[25]='/lib/splash/cache/levels /lib/splash/cache/message /lib/splash/cache/progress' SEC_MOD_2[25]=' -cm' RULENAME[26]='User Bin and Lib Directories' FILELIST[26]='/usr/bin /usr/sbin /usr/local/bin /usr/local/games /usr/local/sbin /usr/lib /usr/local/lib' SEVERITY[26]='60' ETCSECVALUE[26]=ReadOnly RECURSE[26]=' (recurse = 1)' RULENAME[27]='Log Files' COMMENTS[27]='These files change inode, and grow' FILELIST[27]='/var/log/critical /var/log/messages /var/log/*g' SEVERITY[27]='60' SEC_MOD[27]=' -i' # Root User Directory Rule uses multiple filelists # FILELIST_x variable names MUST be sequential, starting with "x" = "2" # Available correlated variables are COMMENTS_x, SEC_MOD_x, and RECURSE_x # Output of Root User rule approximately resembles RedHat legacy policy file # Todo: Some files in /root are apt to change, tweak and explain policy # Stifling creation of .xauth????? files is done under xauth program # These files are made by action of a regular user, who uses `su` # The regular user can stifle creation of .xauth???? files in /root # by adding "export XAUTHORITY=.xauth" in the regular user's .bashrc # /root/.xauthIxxb8F [added, 52 bytes] # /root/.xauthhDMbvC [removed, 52 bytes] RULENAME[28]='Root User Directory' IGNORLST[28]="/root/.lesshst /root/.bash_history \ /root/.aumixrc /root/.calc_history \ /root/.fonts.cache-1 \ /root/.lynx_cookies \ /root/.mysql_history \ /root/.sc_history \ /root/.stack.wcd /root/.treedata.wcd /root/bin/wcd.go" COMMENTS[28]='Config and files for console applications' FILELIST[28]="/root \ /root/.bashrc /root/.bash_profile /root/.bash_logout \ /root/.cshrc /root/.tcshrc /root/.screenrc \ /root/.htoprc /root/.mc /root/.ncftp \ /root/Mail /root/mail \ /root/.elm /root/.pinerc /root/.pinepwd \ /root/.mailcap /root/.mime.types \ /root/.addressbook.lu /root/.addressbook /root/.sendxmpprc \ /root/.links /root/.lynxrc \ /root/.riprc /root/.sversionrc \ /root/.esd_auth" COMMENTS_2[28]='Action in these directories will trigger a warning' FILELIST_2[28]="/root/bin /root/.ssh /root/.amandahosts /root/.gnupg" SEC_MOD_2[28]=' +srbmcCM' COMMENTS_3[28]='X-Windows should not be run as Root User!' FILELIST_3[28]="/root/.ICEauthority /root/.xsession-errors /root/.Xresources /root/.Xmodmap \ /root/.config /root/.enlightenment /root/.fltk /root/.fvwm /root/.fvwmrc \ /root/.gconf /root/.gconfd \ /root/.gnome /root/.gnome2 /root/.gnome_private /root/.gnome-desktop \ /root/.qt /root/.sawfish \ /root/.xauth" COMMENTS_4[28]='Files that change inode number' FILELIST_4[28]='/root/.Xauthority' SEC_MOD_4[28]=' -i' # Todo: Files in /var/run are apt to change, tweak and explain policy # /var/run/dovecot/login/ssl-parameters.dat [230 bytes, modified, world readable] RULENAME[29]='System Boot Changes' COMMENTS[29]='Legacy from RedHat Policy File: These files change every time the system boots' FILELIST[29]='/etc/ioctl.save /etc/.pwd.lock /var/lock/subsys' IGNORLST[29]='/var/run/screen /var/run/dovecot' COMMENTS_2[29]='Files that change inode number' FILELIST_2[29]='/etc/mtab /var/run' SEC_MOD_2[29]=' -i' RULENAME[30]='Security Control File' FILELIST[30]='/etc/security' ETCSECVALUE[30]='ReadOnly' ########### End Default Package Lists and RuleName Definitions # "select_policy" routine runs each filename through a gauntlet # $Filetype assignment is based on which attribute matched last. select_policy () { Filetype=Config [ -n "`expr $targetfile : '\(/etc/\)'`" ] && Filetype=Config [ -n "`expr $targetfile : '\(/lib/\)'`" ] && Filetype=Lib [ -n "`expr $targetfile : '\(/log/\)'`" ] && Filetype=Log [ -n "`expr $targetfile : '\(/root/\)'`" ] && Filetype=RootFile [ -n "`expr $targetfile : '\(/lib/modules\)'`" ] && Filetype=Kernel [ -n "`file -b $targetfile | grep kernel`" ] && Filetype=Kernel [ -x $targetfile ] && Filetype=Bin [ -b $targetfile ] && Filetype=Block [ -c $targetfile ] && Filetype=Char [ -d $targetfile ] && Filetype=Dir [ -n "`expr $targetfile : '\(/dev/tty\)'`" ] && Filetype=Tty [ $targetfile == "/root" ] && Filetype=RootDir [ -u $targetfile -o -g $targetfile ] && Filetype=SUID case $Filetype in RootDir ) echo "-> \$(IgnoreNone)-amcSH ; # Catch additions to /root" ;; Dir ) echo "-> \$(${ETCSECVALUE[$i]:-Dynamic})${SEC_MOD[$i]}${RECURSE[$i]} ;" ;; RootFile ) echo "-> \$(${ETCSECVALUE[$i]:-Dynamic})${SEC_MOD[$i]} ;" ;; Config ) echo "-> \$(${ETCSECVALUE[$i]:-Dynamic})${SEC_MOD[$i]} ;" ;; Kernel ) echo "-> \$(${BINSECVALUE[$i]:-ReadOnly})${SEC_MOD[$i]} ;" ;; Bin ) echo "-> \$(${BINSECVALUE[$i]:-ReadOnly})${SEC_MOD[$i]} ;" ;; Lib ) echo "-> \$(${BINSECVALUE[$i]:-ReadOnly})${SEC_MOD[$i]} ;" ;; Log ) echo "-> \$(${LOGSECVALUE[$i]:-Growing})${SEC_MOD[$i]} ;" ;; SUID ) echo "-> \$(IgnoreNone)-aSH ;" ;; Tty ) echo "-> \$(Dynamic)-ipug ; # user and group change on access" ;; Char ) echo "-> \$(Device) ;" ;; Block ) echo "-> \$(Device) ;" ;; esac } print_header () { echo echo " #########################################################################" echo " # #" echo " # Tripwire Policy File #" echo " # #" echo " #########################################################################" echo echo echo " # Generated by $0" echo " # Version $VERSION" echo " # `date '+%B %e, %Y at %R'`" echo " # http://bugs.gentoo.org/344577" echo echo echo " #########################################################################" echo " # #" echo " # Global Variable Definitions #" echo " # File locations established at installation of tripwire #" echo " # #" echo " #########################################################################" echo echo "@@section GLOBAL" echo "HOSTNAME=\"`hostname`\" ; # Should be the hostname of this system" echo "TWPOL=\"${TWPOL}\" ;" echo "TWKEY=\"${TWKEY}\" ;" echo "TWDB=\"${TWDB}\" ;" echo "TWREPORT=\"${TWREPORT}\" ;" echo echo " #########################################################################" echo " # #" echo " # Security Aliases #" echo " # #" echo " #########################################################################" echo echo " # Standard Tripwire File Property Mask Aliases (\`man twpolicy\`)" echo echo " # ReadOnly +pinugtsdbmCM-rlacSH" echo " # Dynamic +pinugtd-srlbamcCMSH" echo " # Growing +pinugtdl-srbamcCMSH" echo " # Device +pugsdr-intlbamcCMSH" echo " # IgnoreAll -pinugtsdrlbamcCMSH" echo " # IgnoreNone +pinugtsdrbamcCMSH-l" echo echo " # Non-standard File Property Mask Aliases" echo echo '@@section FS' echo ' Invariant = +pugt ; # Watch permissions, UID, GID, and filetype' echo echo '#=================== [ Begin Hardcoded Tripwire Rules ] ======================' echo echo '(' echo ' rulename = "Tripwire Data Files",' echo ' severity = 100' echo ')' echo '{' echo ' # Tripwire creates backup files by renaming tw.pol and tw.cfg' echo ' # then creating new files. The new files have new inode numbers.' echo echo ' $(TWDB) -> $(Dynamic) -i ;' echo ' $(TWPOL)/tw.pol -> $(ReadOnly) -i ;' echo ' $(TWPOL)/tw.cfg -> $(ReadOnly) -i ;' echo ' $(TWKEY)/$(HOSTNAME)-local.key -> $(ReadOnly) ;' echo ' $(TWKEY)/site.key -> $(ReadOnly) ;' echo echo ' # Do not scan individual `tripwire --check` reports' echo ' $(TWREPORT) -> $(Dynamic) (recurse = 0) ;' echo '}' echo echo '#=================== [ Begin Generated Tripwire Rules ] ======================' echo } print_footer () { echo echo '#=============================================================================' echo '#' echo '# Hardcoded and generated output is based on:' echo '#' echo '# - tripwire.pol.gentoo : Darren Kirby : September 5, 2006' echo '# http://bugs.gentoo.org/34662' echo '# - Policy file for Red Hat Linux : V1.2.0rh : August 9, 2001' echo '# - FreeBSD: ports/security/tripwire/files/twpol.txt : v 1.3 : 2005/08/09' echo '# http://lists.freebsd.org/pipermail/freebsd-security/2005-October/003221.html' echo '# - Examples found in tripwire-2.4.2-src.tar.bz2 source code distribution' echo '#' echo '# Some examples note a relationship to work Copyright 2000 by Tripwire, Inc.' echo '#' echo '# Tripwire is a registered trademark of Tripwire, Inc.' echo '# (in the United States and other countries)' echo '# All rights reserved.' echo '#' echo '# FreeBSD is a registered trademark of the FreeBSD Project Inc.' echo '# Red Hat is a registered trademark of Red Hat, Inc.' echo '#' echo '#=============================================================================' echo '#' echo '# Tripwire, Inc. permission statements ...' echo '#' echo '# Permission is granted to make and distribute verbatim copies of this document' echo '# provided the copyright notice and this permission notice are preserved on all' echo '# copies.' echo '#' echo '# Permission is granted to copy and distribute modified versions of this' echo '# document under the conditions for verbatim copying, provided that the entire' echo '# resulting derived work is distributed under the terms of a permission notice' echo '# identical to this one.' echo '#' echo '################# END of tripwire Policy Text File #################' } # ------- Cycle through RULENAME variable arrays # "print_generated_rules" routine cycles each group of array variables through "print_a_rule" # $[(10#${rule_count}-$i)] would count down, rather than up print_generated_rules () { rule_count=${#RULENAME[@]} for (( i = 0 ; i < rule_count ; i++ )) do [ "$UPDATETW" == "Yes" -a -z "$QUIET" -a -z "$VERBOSE" ] && \ echo -n -e "\\rProcessing rule $i of $[(10#${rule_count}-1)] rules" >&2 print_a_rule done } # "print_a_rule" routine runs once for each RULENAME[] # - make the header for the tripewire rule, including optional "emailto" field # - print ignorefiles, if any # - forward installed package names, one-by-one, to extract_package_filenames # - forward filelists, if any, to process_filelist print_a_rule () { echo echo "################################################################" echo "# RuleName: ${RULENAME[$i]}" [ -n "${PACKAGES[$i]}" ] && echo "# Packages: ${PACKAGES[$i]}" [ -n "${FILELIST[$i]}" ] && echo "# FileNames: ${FILELIST[$i]}" echo "################################################################" echo \( echo " rulename = \"${RULENAME[$i]}\"," echo -n " severity = ${SEVERITY[$i]:-100}" [ -n "${EMAILTO[$i]}" ] && echo -e ",\\n emailto = ${EMAILTO[$i]}" || echo echo \) echo \{ [ -n "${IGNORLST[$i]}" ] && echo -e "\\n# ${RULENAME[$i]}: Ignore changes to these files" for targetfile in ${IGNORLST[$i]} do [ -e "$targetfile" ] && echo " !$targetfile ;" done for package in ${PACKAGES[$i]} do process_packagename done [ -n "${FILELIST[$i]}" ] && process_filelist # Code to process pseudo-two-dimesional arrays # FLST, CMTS, SCMD, and RCRS hold numerically-specific variable names, # Those variable names are in the style of an array name, e.g., FILELIST_2[26] # The specific variable names are then indirectly expanded to their contents for j in {2..100}; do FLST=FILELIST_$j[$i] CMTS=COMMENTS_$j[$i] SCMD=SEC_MOD_$j[$i] RCRS=RECURSE_$j[$i] FILELIST[$i]="${!FLST}" COMMENTS[$i]="${!CMTS}" SEC_MOD[$i]="${!SCMD}" RECURSE[$i]="${!RCRS}" [ -n "${FILELIST[$i]}" ] && process_filelist || break done echo \} } # "process_packagename" routine is applied to every listed package name # - detect whether or not the package is installed (e.g., zero-size $TMP_FILE) # - put list of package-installed files into $TMP_FILE # - pass control to extract_package_filenames # Should be able to genericize the policy generator beyond Gentoo # # Future generic solution probably requires separate query-package commands, # one for checking presence and another for checking contents of package # ---------- ---------------------- ------------ # Distro QUERY_DISTRO Command Comments # ---------- ---------------------- ------------ # Gentoo `qlist -e -o` # known to work # Gentoo `equery --quiet files` # known to work, but slow # Arch Linux `pacman --quiet --list` # appears similar to equery # Debian `dpkg --listfiles` # Paludis `cave contents` # requires script adaptation # Red Hat `rpm -ql` # FreeBSD `pkg_info --quiet -L` # -E tests for existence process_packagename () { $QUERY_DISTRO $package > $TMP_FILE [ -s "$TMP_FILE" ] && extract_package_filenames } # "extract_package_filenames" routine is used only for installed package names. # process_packagename obtained a list of all files installed by the package. # # Only filenames with "bin/", "/etc/", or "/var/log/" are included in output. # Directory names and zero-size files are excluded from output. # # Adding "/lib/.*[.]s[ho]" adds substantial bulk to the generated policy file. # Adding "/lib/rcscripts/[acns]" would be Gentoo-centric (awk|conf.d|net|sh), # - the same list is obtained by adding "/lib/rcscripts/*/*" to FILELIST[] extract_package_filenames () { echo echo "# ${RULENAME[$i]}: $package" echo for targetfile in `grep -e /etc/ -e bin/ -e /var/log/ $TMP_FILE` do [ ! -d $targetfile -a -s $targetfile ] && { output_line select_policy } done } # "process_filelist" routine is used only for filelists. # - outputs comments for the list, if any # - calls for printing each filename and tripwire policy # - blocks listing of directory entries from the /proc/* wildcard process_filelist () { echo -e "\\n# ${RULENAME[$i]}: ${COMMENTS[$i]}\\n" for targetfile in ${FILELIST[$i]} do if [ -d "$targetfile" -a -n "`expr $targetfile : '\(/proc/\)'`" ]; then true elif [ -d "$targetfile" -a -n "`expr $targetfile : '\(/lost+found\)'`" ]; then true elif [ -e $targetfile ]; then output_line select_policy fi done } # "output_line" routine adds a variable number of tabs to obtain alignment # The width of the targetfile name is increased by 2 to account for indent # The maximum number of additional tabs is the digit after "10#" # The width of the TAB is taken as 8 characters output_line () { MAKE_TABS=$[(10#4-(${#targetfile}+2)/8)] # Calculate number of TABs echo -n " $targetfile" echo -e -n \\t # Output at least one TAB for (( z = 0 ; z < MAKE_TABS ; z++ )) # Up to five TABs do echo -e -n \\t done } ################################################################# # Top Routine for Generating Policies # ################################################################# print_policy_text () { print_header print_generated_rules print_footer } ################################################################# # Routines for the user interface # Structure and functions should be clear on inspection ################################################################# qlist_error_exit () { echo echo This script uses qlist to obtain filenames for Gentoo packages >&2 echo On a Gentoo system, \`emerge portage-utils\` >&2 echo Continuing even though qlist is not found on this system ... >&2 echo [ "$DEBUGME" == "y" ] || sleep 5 } tripwire_error_exit () { echo echo "This script has no known function aside from tripwire." >&2 echo " On Gentoo, \`emerge tripwire\`" >&2 echo "Continuing even though tripwire is not found on this system ..." >&2 echo [ "$DEBUGME" == "y" ] || sleep 5 } var_log_warning () { echo echo " ############ !!!! WARNING !!!! ############" echo " # Default policy for /var/log directory watches ONLY: #" echo " # /var/log/critical /var/log/messages /var/log/*g #" echo " ########################################################" } recite_ver () { echo echo "This is `basename $0` version $VERSION" echo "A Gentoo-oriented Tripwire Policy Generator" echo } recite_help () { recite_ver echo "Usage: `basename $0` [-c configfile] [-u[-q|-v][-r]] [-h] [-V] [debug [#]]" echo echo " -c Read RULENAME[], PACKAGELIST[] and FILELIST[] from configfile" echo " -u Tripwire policy and database updated after generating policy text file" echo " -r Remove policy text file after \`twadmin --create-polfile\` completes" echo " -q Quiet - stifle display of rule count as rulesets are processed" echo " -v Verbose - display policy text generation" echo " -h Output this version and help information" echo " -V Output version information" echo echo "When invoked with no command-line parameter:" echo " - output from `basename $0` is directed to STDOUT" echo echo "When invoked with -u command line parameter:" echo " - output from `basename $0` is directed to a file in the $TWPOL directory" echo " - the command \`twadmin --create-polfile\` creates tw.pol from that file" echo " - the command \`tripwire --init\` creates tripwire database from tw.pol" echo echo "When invoked with debug command line parameter:" echo " - output from `basename $0` is limited to one rule, default RULENAME[0]" echo exit } # Test "existence" of config file - note that test is for regular file # Unset all the arrays set above, to avoid corruption of external configuration read_external_config () { if [ -f "$CONFIG_FILE" ]; then unset RULENAME SEVERITY EMAILTO \ BINSECVALUE ETCSECVALUE LOGSECVALUE \ IGNORLST \ PACKAGES \ FILELIST FILELIST_2 FILELIST_3 FILELIST_4 \ COMMENTS COMMENTS_2 COMMENTS_3 COMMENTS_4 \ SEC_MOD SEC_MOD_2 SEC_MOD_3 SEC_MOD_4 \ RECURSE RECURSE_2 RECURSE_3 RECURSE_4 source "$CONFIG_FILE" else echo echo "External configuration file, $CONFIG_FILE, does not exist." echo "Exiting `basename$0`" exit 2 fi } update_tripwire_policy () { echo echo Completed creation of $TRIPWIRE_POL echo echo To create encrypted tripwire policy file \($TWPOL/tw.pol\), run: echo twadmin --create-polfile $TRIPWIRE_POL echo echo Then, to create tripwire database ${TWDB}/${HOSTNAME}.twd, run: echo tripwire --init echo echo -n "Take those steps now? [Y/n]: " read -n 1 -t 20 RUN_TRIPWIRE if [ "${RUN_TRIPWIRE,N}" == "n" ]; then echo Skipping creation of tripwire policy and database. Goodbye. else echo echo Creating new $TWPOL/tw.pol from $TRIPWIRE_POL ... twadmin --create-polfile $TRIPWIRE_POL if [ "${REMOVE_POL}" == "Yes" ]; then echo echo Per your instructions, deleting $TRIPWIRE_POL now ... rm -f $TRIPWIRE_POL echo fi tripwire --init fi } mode_auto_update () { if [ ! -d "$TWPOL" ]; then echo Tripwire update function depends on existence of the directory $TWPOL echo Running `basename $0` with no parameters prints tripwire policy to STDOUT echo Exiting. exit 1 fi TRIPWIRE_POL=$TWPOL/twpol-`date +%y%m%d-%H%M`.txt if [ "$VERBOSE" == "Yes" ]; then echo echo Showing creation of $TRIPWIRE_POL echo echo After the policy file is created, you will be prompted to echo invoke tripwire to create and encrypted policy and database echo echo Sleeping 10 seconds ... [ "$DEBUGME" == "y" ] || sleep 10 print_policy_text | tee $TRIPWIRE_POL else echo echo Tripwire policy rules being written to $TRIPWIRE_POL echo This will take a few moments ... print_policy_text > $TRIPWIRE_POL fi update_tripwire_policy } # When invoked without the -u parameter, the message below is sent to STDERR # This message won't appear in redirected output: mktripwire.sh > twpol.txt mode_echo_policy () { echo "`basename $0` v. $VERSION" >&2 echo "Run `basename $0` with -u parameter to automate tripwire update." >&2 echo "Sleeping 5 seconds ..." >&2 [ "$DEBUGME" == "y" ] || sleep 5 echo >&2 echo "Creating the policy text file will take a few moments ..." >&2 echo print_policy_text } user_interface () { if [ "$UPDATETW" == "Yes" ]; then mode_auto_update else mode_echo_policy fi } ################################################################# # Invocation enters here ################################################################# # Process command line input while getopts :c:uqvrhVt OPTION do case $OPTION in c ) CONFIG_FILE=$OPTARG ;; u ) UPDATETW=Yes ;; q ) QUIET=Yes ;; v ) VERBOSE=Yes ;; r ) REMOVE_POL=Yes ;; h ) recite_help ;; V ) recite_ver; exit ;; * ) recite_help ;; esac done shift $(($OPTIND - 1)) # If the user claims use of a config file, test and read it # Otherwise, show a warning that default rule does NOT watch /var/log [ -n "$CONFIG_FILE" ] && read_external_config || var_log_warning # If the user has called for debugging of a rule, # generate output for just that rule, then exit # default rulename[0], but user can debug any single rule [ "$1" == "debug" ] && { DEBUGME=y i=${2:-0} echo echo " !! WARNING !! `basename $0` is in DEBUG Mode!" echo " !! WARNING !! Processing --ONLY-- RULENAME[${i}]" echo print_a_rule echo echo " !! WARNING !! `basename $0` was in DEBUG Mode!" echo " !! WARNING !! Processed --ONLY-- RULENAME[${i}]" exit } # Test for presence of the programs "qlist" and "tripwire" # Run the program for init_error_type in qlist tripwire; do hash $init_error_type 2> /dev/null || ${init_error_type}_error_exit done user_interface ################################################################# ################################################################# # Nothing But Junk Below # Routine Might be Used to Create List of Package Names ################################################################# SYSTEM_FILE_LIST=/root/system-files.txt list_system_files () { for i in `EMERGE_DEFAULT_OPTS="" emerge -peq system | cut -d"]" -f2`; do echo $i # qlist --exact $i done } #echo Making list of system files ... #list_system_files > $SYSTEM_FILE_LIST