#! /bin/bash # /root/bin/mktwpol.sh \ # /etc/tripwire/mktwpol.sh ] Pick a location # /usr/local/sbin/mktwpol.sh / VERSION=0.0.4 # 24 Nov 2010 # c.cboldt at gmail.com # A (mostly) Gentoo-oriented Tripwire Policy Generator # Outputs tripwire policy from lists of application packages # Depends on qlist (in portage-utils) to extract installed filenames # Won't make a tripwire policy entry if a listed package or file is absent # Can be adapted to other package managers; e.g., rpm, pacman, dpkg # Probably depends on a bash shell, certain versions of grep, fmt and sed # All variables, except TWCFG_TXT, can be set in a separate configfile. # The script will operate if twcfg.txt has been removed for security purposes. # Configuration information is extracted from tw.cfg (not the text file), if it exists # TWCFG_TXT sets the directory (/etc/tripwire) for finding tw.cfg or twcfg.txt TWCFG_TXT=/etc/tripwire/twcfg.txt # ============================================================================== # Improvements? rationalize categories and security level settings # more complete/useful lists of packages # better help messages and comments # better error and condition checking # ============================================================================== # Package query variables for -MAYBE FUTURE- adapting of script # See process_packagename routine, now tested ONLY with qlist and equery # Diversion from the script, compare the speed of `qlist` and `equery` # on mobile AMD Athlon(tm) 4 Processor @ 900 MHz: # qlist -> real 1m 43.399s # equery -> real 9m 6.620s QUERY_DISTRO="qlist --exact" # Gentoo - app-portage/portage-utils # QUERY_DISTRO="equery --quiet files" # Gentoo - app-portage/gentoolkit # QUERY_DISTRO="pacman --quiet --list" # Arch Linux # QUERY_DISTRO="rpm --query --quiet PACKAGE_NAME --list" # Red Hat, OpenSuSE, others # QUERY_DISTRO="dpkg-query --info" # Debian, Ubuntu ( | cut -c2 | grep i ) # QUERY_DISTRO="pkg_info --quiet -E" # FreeBSD QUERY_PACKAGE="${QUERY_DISTRO}" # Works when output is NUL on package absence # QUERY_PACKAGE="dpkg-query -L" # Debian, Ubuntu # QUERY_PACKAGE="pkg_info --quiet -L" # FreeBSD (might work similar to qlist) # QUERY_PACKAGE="cave contents" # Paludis (this would be a challenge) # ============================================================================== # Brief description of array variables used to generate tripwire policies # ======================================================================= # RULENAME[] Rule Name, unique to avoid errors by tripwire --init # PACKAGES[] Optional list of 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 # SEVERITY[] defaults to 100 - written rule-by-rule # EMAILTO[] defaults to empty - written rule-by-rule # # Optional rule-by-rule Security Aliases for File Property Inspection Masks # ========================================================================= # BIN_SEC[] defaults to $(ReadOnly) # ETC_SEC[] defaults to $(Dynamic) - also applies to Directories # LOG_SEC[] defaults to $(Growing) # # Optional list-by-list modifications to default or defined Inspection Masks # ========================================================================== # SEC_MOD[] defaults to empty - written file-by-file across PACKAGE or FILELIST # SFT_MOD[] defaults to empty - when assigned, applies only to SoftLinks # RECURSE[] defaults to (recurse=true) - when assigned, applies only to Directories # There are also variables for multiple filelists within a single rule # - Useful for specializing Comments and Inspection Masks # - See Root User Directory Rule for multiple filelists example # FILELIST_x variable names MUST be sequential, starting with "x" = "2" # Available correlated variables: COMMENTS_x, SEC_MOD_x, SFT_MOD_x, and RECURSE_x ##### Start of Package and File Lists RULENAME[0]='System Auditing Programs' PACKAGES[0]='tripwire aide chkrootkit lynis nagios openscap osiris rkhunter yasat' EMAILTO[0]='"root@localhost"' ETC_SEC[0]='ReadOnly' # ETC_SEC[] applies to files that `stat` as directory names RULENAME[1]='Invariant Directories' COMMENTS[1]='Owner and group should remain static' FILELIST[1]='/ /home /etc /mnt /opt' SEVERITY[1]='60' ETC_SEC[1]='Invariant' RECURSE[1]=' (recurse = 0)' RULENAME[2]='Temporary Directories' FILELIST[2]='/usr/tmp /var/tmp /tmp' SEVERITY[2]='35' ETC_SEC[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" ETC_SEC[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' ETC_SEC[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 tmpwatch" COMMENTS[19]='User-installed crontabs' FILELIST[19]='/var/spool/cron/crontabs' RULENAME[20]='Boot Selector and Kernel' PACKAGES[20]='grub lilo kccmp kerneloops ksymoops module-rebuild' COMMENTS[20]='Contents of /boot directory are safer on an unmounted partition' FILELIST[20]='/boot/* /lib/modules' COMMENTS_2[20]='Detect current mounting of /boot' FILELIST_2[20]='/boot' SEC_MOD_2[20]=' +mc' 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, not owned by a package, can be found with: # for i in `locate --ignore-case --regexp etc.*local` # 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" ETC_SEC[23]='ReadOnly' # Policy generator avoids opening devices (recursion) by applying # the $(Device) policy for block and character special devices # See "select_policy" routine # Assumed that Inode changes for listed /dev files, most under creation by udev # Device number changes on /dev/files, from "9" to "10", moving from 2.6.34 to 2.6.35 kernel # Inode changes on /proc files, when switching kernel 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[0-9] /dev/tty1[012] \ /dev/urandom /dev/initctl" SEC_MOD[24]=' -i' COMMENTS_2[24]='/proc/mounts softlink undergoes time modification' FILELIST_2[24]='/proc/*' SFT_MOD_2[24]=' -mc' # /lib/splash/cache is a busy directory RULENAME[25]='OS Bin and Lib Directories' IGNORLST[25]='/lib/splash/cache' FILELIST[25]='/bin /sbin /lib' ETC_SEC[25]='ReadOnly' # /usr/lib/pkgconfig directory changes as packages are added/removed 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' ETC_SEC[26]='ReadOnly' RECURSE[26]=' (recurse = 1)' # logrotate determines the way log files are made and removed # logrotate.conf 'nocreate' option can result in alarms on missing log files RULENAME[27]='Log Files' COMMENTS[27]='`logrotate` may change logfile inodes' FILELIST[27]='/var/log/critical /var/log/messages /var/log/*g' SEVERITY[27]='60' SEC_MOD[27]=' -i' # Output of Root User rule approximately resembles RedHat legacy policy file # 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 # but this affects display opening permission under X-win! 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' RULENAME[29]='Security Control File' FILELIST[29]='/etc/security' ETC_SEC[29]='ReadOnly' # Todo: Files in /var/run are apt to change. Tweak and explain policy # Use (recurse = 1), rather than IGNORLST, to limit range of inspection RULENAME[30]='System Boot Changes' COMMENTS[30]='Many files change inode number' FILELIST[30]='/etc/mtab /var/run /var/run/dhcpcd /var/run/sudo' SEC_MOD[30]=' -i' RECURSE[30]=' (recurse = 1)' COMMENTS_2[30]='RedHat Policy File: These files change every time the system boots' FILELIST_2[30]='/etc/ioctl.save /etc/.pwd.lock /var/lock/subsys' ########### 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 [ -p $targetfile ] && Filetype=Pipe [ -S $targetfile ] && Filetype=Socket [ -d $targetfile ] && Filetype=Dir [ -h $targetfile ] && Filetype=SoftLink [ -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 "-> \$(${ETC_SEC[$i]:-Dynamic})${SEC_MOD[$i]}${RECURSE[$i]} ;" ;; RootFile ) echo "-> \$(${ETC_SEC[$i]:-Dynamic})${SEC_MOD[$i]} ;" ;; Config ) echo "-> \$(${ETC_SEC[$i]:-Dynamic})${SEC_MOD[$i]} ;" ;; Kernel ) echo "-> \$(${BIN_SEC[$i]:-ReadOnly})${SEC_MOD[$i]} ;" ;; Bin ) echo "-> \$(${BIN_SEC[$i]:-ReadOnly})${SEC_MOD[$i]} ;" ;; Lib ) echo "-> \$(${BIN_SEC[$i]:-ReadOnly})${SEC_MOD[$i]} ;" ;; SoftLink ) echo "-> \$(SoftLink)${SFT_MOD[$i]} ; # Softlink" ;; Log ) echo "-> \$(${LOG_SEC[$i]:-Growing})${SEC_MOD[$i]} ;" ;; SUID ) echo "-> \$(IgnoreNone)-aSH ; # SUID or SGID" ;; Tty ) echo "-> \$(Dynamic)-ipug ; # user and group change" ;; Char ) echo "-> \$(Device) ; # Character device" ;; Block ) echo "-> \$(Device) ; # Block device" ;; Pipe ) echo "-> \$(Device) ; # Pipe" ;; Socket ) echo "-> \$(Device) ; # Socket" ;; esac } print_header () { echo echo " # Tripwire Policy File http://bugs.gentoo.org/344577" echo " # ===================================================" echo " # Generated by $0" echo " # Version $VERSION" echo " # `date '+%B %e, %Y at %R'`" echo echo "@@section GLOBAL" echo "HOSTNAME=\"`hostname`\" ; # Make this the hostname of this system" 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 '@@section FS' echo echo ' # Non-standard File Property Mask Aliases' echo ' # --------------------------------------------' echo ' Invariant = +pugt ; # Permissions, UID, GID, and filetype' echo ' SoftLink = +pinugtsdbmc ; # Skip checking hash values' echo echo '#=================== [ Begin Hardcoded Tripwire Rules ] ======================' echo echo '(' echo ' rulename = "Tripwire Data Files",' echo ' severity = 100' echo ')' echo '{' echo " # Tripwire file locations here, were transposed from ${TWCFG_TXT}" echo ' # Tripwire creates backup files by renaming tw.cfg and tw.pol,' echo ' # then creating new files. The new files have new inode numbers.' echo " # Database and backup files in ${DBDIR} do not appear to change inode." echo echo " ${TWCFG_DIR}/tw.cfg -> \$(ReadOnly) -i ;" echo " ${POLFILE} -> \$(ReadOnly) -i ;" echo " ${SITEKEYFILE} -> \$(ReadOnly) ;" echo " ${LOCALKEYFILE} -> \$(ReadOnly) ;" echo " ${DBDIR} -> \$(Dynamic) ;" echo echo ' # Do not scan individual `tripwire --check` reports' echo echo " ${REPORTDIR} -> \$(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 '# 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 "\\r Processing 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 tripwire 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]}" | fmt -u | sed s/^/"# "/ [ -n "${FILELIST[$i]}" ] && echo "FileNames: ${FILELIST[$i]}" | fmt -u | sed s/^/"# "/ 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\\n" 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-dimensional arrays. # FLST, CMTS, SCMD, SFMD, 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] SFMD=SFT_MOD_$j[$i] RCRS=RECURSE_$j[$i] FILELIST[$i]="${!FLST}" COMMENTS[$i]="${!CMTS}" SEC_MOD[$i]="${!SCMD}" SFT_MOD[$i]="${!SFMD}" RECURSE[$i]="${!RCRS}" [ -n "${FILELIST[$i]}" ] && process_filelist || break done echo \} } # "process_packagename" routine is applied to every listed package name # - detects whether or not the package is installed (e.g., zero-size $TMP_FILE) # - puts list of package-installed files into $TMP_FILE # - passes control to extract_package_filenames routine, which reads $TMP_FILE # The process_packagename function can be made generic, beyond Gentoo # # Future generic solution probably requires separate query-package commands, # one for checking presence and another for checking contents of package # ---------- ----------------------- ------------ # Distro QUERY_PACKAGE Command Comments # ---------- ----------------------- ------------ # Gentoo `qlist --exact` # known to work # Gentoo `equery --quiet files` # known to work, but slow # Arch Linux `pacman --quiet --list` # maybe similar to qlist/equery # Debian `dpkg-query --listfiles` # requires script adaptation # FreeBSD `pkg_info --quiet -L` # -E tests for existence # Paludis `cave -c no contents` # requires script adaptation # Red Hat `rpm -ql` process_packagename () { if [ "$QUERY_PACKAGE" != "$QUERY_DISTRO" ]; then # $QUERY_DISTRO $package # if package does not exist, return (skipping $QUERY_PACKAGE step) true fi $QUERY_PACKAGE $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 [ -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 ################################################################# # Messages sent to STDERR won't appear in redirected output: mktwpol.sh > twpol.txt qlist_exist_error () { echo " ${PKG_LISTER} not found. Package names will not be processed." >&2 echo " On a Gentoo system, \`emerge portage-utils\` to get qlist" >&2 echo " Continuing even though ${PKG_LISTER} was not found ..." >&2 echo >&2 QUERY_PACKAGE=true # Dummy command sleep 5 # Prints NUL output at each package name } tripwire_exist_error () { echo " This script has no known function aside from tripwire." >&2 echo " On Gentoo, \`emerge tripwire\`" >&2 echo " Continuing even though tripwire was not found ..." >&2 echo >&2 sleep 5 } default_rule_warning () { echo echo " ############ !!!! WARNING !!!! ############" echo " # Default policy for /var/log watches ONLY ... #" echo " # /var/log/critical /var/log/messages /var/log/*g #" echo " ########################################################" echo } recite_ver () { echo echo " This is ${0##*/} version $VERSION" # bash `basename` equivalent echo " A Gentoo-oriented Tripwire Policy Generator" echo } recite_help () { recite_ver echo " Usage: ${0##*/} [-c configfile] [-u[-r][-q|-v]] [-h] [-V] [debug [#]]" echo echo " -c Read RULENAME[], PACKAGELIST[] and FILELIST[] from configfile" echo " -u Create tripwire policy and database 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 ${0##*/} is directed to STDOUT" echo echo " When invoked with \"-u\" command line parameter:" echo " - output from ${0##*/} is directed to a file in the tripwire config directory" echo " - the command \`twadmin --create-polfile\` creates tw.pol from that file" echo " - the command \`tripwire --init\` creates a tripwire database from tw.pol" echo echo " When invoked with \"debug\" command line parameter:" echo " - output from ${0##*/} is limited to one rule, default RULENAME[0]" echo exit } # Test "existence" of external 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 \ BIN_SEC ETC_SEC LOG_SEC \ 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 \ SFT_MOD SFT_MOD_2 SFT_MOD_3 SFT_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 ${0##*/}" exit 2 fi } # Bash shell facility. Echos/assigns the second parm, skipping the first character. # For example, "POLFILE =/etc/tripwire/tw.pol" becomes "/etc/tripwire/tw.pol" # Variable has to be exported to be visible beyond the "assign_variable" subroutine assign_variable () { export ${variable}=`echo ${2:1}` } # No test for existence of twadmin. User has already been warned about # absence of tripwire, and a text config file is an alternative # tw.cfg filename is hardcoded into tripwire, so okay to hardcode it here. read_tw_cfg () { assign_variable `twadmin -m f -c ${TWCFG_DIR}/tw.cfg | grep $variable` } read_twcfg_txt () { assign_variable `grep $variable ${TWCFG_TXT}` } get_twcfg_variables () { echo >&2 if [ -r ${TWCFG_DIR}/tw.cfg ]; then extract_variable=read_tw_cfg echo " Reading tripwire configuration from ${TWCFG_DIR}/tw.cfg" >&2 elif [ -r ${TWCFG_TXT} ]; then extract_variable=read_twcfg_txt echo " Reading tripwire configuration from ${TWCFG_DIR}/twcfg.txt" >&2 else echo " ${0##*/} depends on ${TWCFG_DIR}/tw.cfg (preferred) or ${TWCFG_TXT}" echo " Exiting. Goodbye." exit 3 fi for variable in POLFILE SITEKEYFILE LOCALKEYFILE DBFILE REPORTFILE; do ${extract_variable} done REPORTDIR=${REPORTFILE%/*} # bash shell equivalent to the `dirname` command DBDIR=${DBFILE%/*} } # Check for and add tmpwatch stanza following `tripwire --check` cronjob tmpwatch_cronjob () { # /etc/cron.daily/tripwire.cron is part of Gentoo tripwire package. TMPWATCH_CRON=/etc/cron.daily/tripwire.cron # Skip this step if tmpwatch is already part of ${TMPWATCH_CRON} file [ ! -z "`grep tmpwatch ${TMPWATCH_CRON}`" ] && return echo >&2 echo " tmpwatch can automatically prune old report files." >&2 echo >&2 echo " if [ -d ${REPORTDIR} ]; then" >&2 echo " tmpwatch --ctime 168 ${REPORTDIR}" >&2 echo " fi" >&2 echo >&2 echo -n " Append that stanza to ${TMPWATCH_CRON}? [y/N] " >&2 read -n 1 -t 20 WRITE_TMPWATCH if [ "${WRITE_TMPWATCH,Y}" == "y" ]; then echo " # Added by $0 $VERSION on `date` # Use of tmpwatch to prune tripwire report file directory. if [ -d ${REPORTDIR} ]; then tmpwatch --ctime 168 ${REPORTDIR} fi " >> ${TMPWATCH_CRON} echo >&2 fi } test_tw_setup () { if [ ! -s "${TWCFG_DIR}/tw.cfg" -o ! -s "${SITEKEYFILE}" ]; then if [ -x "${TWCFG_DIR}/twinstall.sh" ]; then ${TWCFG_DIR}/twinstall.sh tmpwatch_cronjob else echo " tripwire will refuse to make its database because" >&2 echo " ${TWCFG_DIR}/tw.cfg or ${SITEKEYFILE} files do not exist." >&2 echo " Continuing even though tripwire is not set-up ..." >&2 echo >&2 fi fi } update_tripwire_policy () { echo echo " To create an encrypted tripwire policy file (${POLFILE})," echo " and tripwire database (in ${DBDIR}/), run:" echo echo " twadmin --create-polfile $TRIPWIRE_POL" 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 twadmin --create-polfile $TRIPWIRE_POL if [ "${REMOVE_POL}" == "Yes" ]; then echo echo -n " Do you still want to delete $TRIPWIRE_POL? [Y/n]: " read -n 1 -t 15 YES_IM_SURE if [ "${YES_IM_SURE,N}" == "n" ]; then true else rm -f $TRIPWIRE_POL fi echo fi tripwire --init fi } mode_auto_update () { if [ ! -d "$TWCFG_DIR" ]; then echo Tripwire update function depends on existence of the directory $TWCFG_DIR echo Running ${0##*/} with no parameters prints tripwire policy to STDOUT echo Exiting. exit 1 fi TRIPWIRE_POL=$TWCFG_DIR/twpol-`date +%y%m%d-%H%M`.txt if [ "$VERBOSE" == "Yes" ]; then echo " Showing creation of $TRIPWIRE_POL" echo echo " After the policy file is created, you will be prompted to" echo " invoke twadmin to create an encrypted policy and database" echo echo " Sleeping 10 seconds ..." sleep 10 print_policy_text | tee $TRIPWIRE_POL else echo " Tripwire policy rules being written to $TRIPWIRE_POL" echo " This will take a few moments ..." print_policy_text > $TRIPWIRE_POL echo fi update_tripwire_policy } mode_echo_policy () { echo " Run ${0##*/} -h to read about updating the tripwire database." >&2 echo " ${0##*/} v. $VERSION is sleeping for 5 seconds ..." >&2 sleep 5 echo >&2 echo " Creating the policy text will take a few moments ..." >&2 echo print_policy_text } mode_debug () { DEBUGME=y # Unused variable. Might be useful for internal debugging. i=${1:-0} # default rulename[0], but user can debug any single rule echo echo " !! WARNING !! ${0##*/} is in DEBUG Mode !!" echo " !! WARNING !! Processing --ONLY-- RULENAME[${i}]" echo print_a_rule echo echo " !! WARNING !! ${0##*/} was in DEBUG Mode !!" echo " !! WARNING !! Processed --ONLY-- RULENAME[${i}]" exit } ################################################################# # Invocation enters here ################################################################# # TMP_FILE contains a list of files associated with an installed package # TWCFG_DIR is used in several subroutines TMP_FILE=/tmp/mktwpol.tmp TWCFG_DIR=${TWCFG_TXT%/*} # bash `dirname` equivalent # 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)) # Test for presence of tripwire executable # Get tripwire variables from twcftg.txt file hash tripwire 2> /dev/null || tripwire_exist_error get_twcfg_variables # Test for tw.cfg and site.key files # If either does not exist, call twinstall.sh to make tw.cfg and key files test_tw_setup # If the user claims use of an external config file, test and read it # If using internal rules, print warning relating to default rules [ -n "$CONFIG_FILE" ] && read_external_config || default_rule_warning # Test for presence of program to extract file names from package names PKG_LISTER=`expr "${QUERY_PACKAGE}" : '\(.* \)'` hash ${PKG_LISTER} 2> /dev/null || qlist_exist_error # Debug mode has priority. $2 is the optional rule number; `mktwpol.sh debug 10` [ "$1" == "debug" ] && mode_debug $2 # Generate a tripwire policy text file if [ "$UPDATETW" == "Yes" ]; then mode_auto_update else mode_echo_policy fi # =========================================================================================== # Version # Remarks and Changes # =========================================================================================== # 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 # 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 facilities for future adaptation to other package managers; see: # http://www.syntaxtechnology.com/2009/07/install-tripwire-on-fedora-11 # 0.0.4 # 101124 Sent to http://bugs.gentoo.org/344577 # Changed name from mktripwire.sh to mktwpol.sh # Fixed bug where /log/*, but not /var/log/*, was assigned Filetype=Log # Fixed bug where /lib/*, but not /usr/log/*, was assigned Filetype=Lib # Moved some functions to subroutines to simplify view of program structure # Shuffled changes record to bottom of file. Numerous remark revisions. # Import tripwire setup variables from tw.cfg or twcfg.txt file # Printing of long package or file lists broken into multiple lines # Added test for tw.cfg, site.key, and conditionally running twinstall.sh # Added option to add tmpwatch lines to /etc/cron.daily/tripwire.cron # Changed variable names BINSECVALUE[] to BIN_SEC[], etc. # Added rules for socket, pipe, and soft-link files # Added SFT_MOD[] variable to subtract time check from /proc/mounts softlink # Made "presence of package query program" test generic # Made numerous policy changes, especially in /var/run