#! /bin/bash # /usr/local/sbin/mktwpol.sh [[ "${EUID}" != "0" ]] && echo " Got root?" && exit [[ ! "${BASH}" =~ "bash" ]] && echo " ${0##*/} needs bash" && exit VERSION=14APR11 # c.cboldt at gmail.com # A Gentoo-oriented Tripwire Policy Text Generator # Creates tripwire policy text from lists of package names # Obtains lists of files associated with a package by reading from /var/db/pkg # See mktwpol-generic.sh for script that is adaptable to other package managers # # - Make sure packages critical to YOUR system are named in a PACKAGES[] list !! # (many package names are noted in default lists, but my view is myopic) # - Optional mktwpol.cfg (or other) script configuration file can be used to: # - set default command-line switches # - substitute, augment, or modify package lists and file lists # Tripwire configuration is extracted from tw.cfg (not the text file), if it exists. # The twcfg.txt file can be removed, for (cough, cough) security purposes. # The script reads from twcfg.txt, when tw.cfg hasn't been created yet. TW_CFG=${TW_CFG:=/etc/tripwire/tw.cfg} # 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 FileList # SEVERITY[] defaults to 100 - written rule-by-rule # EMAILTO[] defaults to empty - written rule-by-rule # Note: This e-mail depends on tripwire's `--email-report` # # Optional rule-by-rule Security *Aliases* for File Property Inspection Masks # =========================================================================== # BIN_SEC[] defaults to ReadOnly - also applies to files in lib/ # 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 # EXCEPT[] defaults to empty - "special" file(s) in expanded wildcard list # SEC_EX[] defaults to empty - *non-alias* security rule for "special" files # The script handles variables for multiple filelists within a single rule # - Useful for specializing Comments and Inspection Masks # 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 Default Package Lists and RuleName Definitions # Note: Script configuration file can replace or augment the default set of rules RULENAME[0]='System Auditing Programs' PACKAGES[0]="tripwire aide chkrootkit fwlogwatch logwatch lynis nagios openscap \ osiris rkhunter sleuthkit yasat" EMAILTO[0]='"root@localhost"' ETC_SEC[0]='ReadOnly' COMMENTS_2[0]='Audit programs data directories' FILELIST_2[0]="/var/lib/rkhunter/db" RECURSE_2[0]=' (recurse = 0)' SEC_MOD_2[0]=' -sbmcCM' RULENAME[1]='Invariant Directories' COMMENTS[1]='Owner and group should remain static' FILELIST[1]='/ /home /etc /mnt /opt' SEVERITY[1]='66' ETC_SEC[1]='Invariant' RECURSE[1]=' (recurse = 0)' RULENAME[2]='Temporary Directories' FILELIST[2]='/usr/tmp /var/tmp /tmp' SEVERITY[2]='33' ETC_SEC[2]='Invariant' RECURSE[2]=' (recurse = 0)' RULENAME[3]='[core|diff|find]utils procps' PACKAGES[3]='coreutils diffutils findutils procps htop lsof' # Disambiguation by including category with a package name is superfluous # The script only prints rules for packages that are installed, and then # only for files that are tested to exist RULENAME[4]='Compression/Archiving Programs' PACKAGES[4]="tar star bzip2 app-arch/gzip zip unzip xz-utils app-arch/lzma sharutils" SEVERITY[4]='66' RULENAME[5]='Network - Setup/Services' PACKAGES[5]="net-tools iproute2 iputils dhcpcd ppp \ madwifi-ng-tools \ bind bind-tools djbdns dnsmasq maradns mydns pdns pdnsd unbound \ distcc mgetty rsync samba telnet-bsd netkit-talk ytalk \ yp-tools ypserv ypbind" ETC_SEC[5]='ReadOnly' RULENAME[6]='Network - Filter/View' PACKAGES[6]="tcpdump tcp-wrappers mrtg netcat nmap wireshark \ iptables iptstate pktstat denyhosts fail2ban knock" ETC_SEC[6]='ReadOnly' RULENAME[7]='Hardware and Device Programs' PACKAGES[7]="udev uam pciutils util-linux psmisc kbd hdparm smartmontools \ lshw ethtool hotplug-base module-init-tools setserial dmraid \ gpart gparted parted partimage ddrescue dd-rescue \ bluez cups" 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 xfsprogs nfs ntfsprogs jfsutils \ sysfsutils autofs lvm2 mdadm mtools fuse sshfs-fuse dosfstools" COMMENTS[8]='From sys-fs/progsreiserfs' FILELIST[8]='/usr/lib/libdal.* /usr/lib/libreiserfs.*' RULENAME[9]='File Manipulation Programs' PACKAGES[9]="gawk grep patch cpio file gettext groff less sys-apps/man mlocate which \ ncurses slang sed slocate patchutils debianutils" RULENAME[10]='Toolchain Programs' PACKAGES[10]='gcc binutils glibc libtool make autoconf automake' COMMENTS[10]='From sys-devel/autoconf-wrapper' FILELIST[10]='/usr/lib/misc/ac-wrapper.sh' RULENAME[11]='Security Related Programs' PACKAGES[11]='shadow sys-libs/pam sudo openldap openssl openssh gnupg gnutls pax-utils' RULENAME[12]='Database Related Programs' PACKAGES[12]='gdbm dev-db/mysql postgresql-server qdbm sqlite sys-libs/db' RULENAME[13]='Programming Languages' PACKAGES[13]='nasm perl dev-lang/python qt-core ruby swig tcl tk expect' SEVERITY[13]='66' RULENAME[14]='MTA Related Programs' PACKAGES[14]='sendmail postfix ssmtp mail-client/mailx procmail dovecot clamav spamassassin' RULENAME[15]='IRC/P2P Related Programs' PACKAGES[15]="net-irc/inspircd irc-server ircservices ngircd ptlink-ircd \ ejabberd jabberd jabberd2 mu-conference" RULENAME[16]='WWW Related Programs' PACKAGES[16]="apache bozohttpd lighttpd mini_httpd thttpd \ dev-haskell/cgi dev-libs/cgicc dev-libs/fcgi \ www-apache/mod_fastcgi www-apache/mod_fcgid www-apache/mod_scgi \ dev-lang/php php-toolkit phpBB" 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' SEVERITY[18]='66' # 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 \ metalog 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 syslinux' 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' # process_packagename routine filters out */lib/* filenames, but captures *.sh # If a packages installs scripts without the .sh suffix, # FILELIST[] can be used to assign rules for scripts installed in /lib/rcscripts RULENAME[22]='Gentoo Specific Programs' PACKAGES[22]='sys-apps/portage portage-utils gentoolkit baselayout openrc sysvinit eix' # COMMENTS[22]='Gentoo-installed scripts' # FILELIST[22]='/lib/rcscripts/*/*' COMMENTS[22]='Local ebuilds - skip "files" subdirs' FILELIST[22]='/usr/local/portage' SEC_MOD[22]=' +M' RECURSE[22]=' (recurse = 3)' COMMENTS_2[22]='/usr/lib/pkgconfig is active at package add/remove' FILELIST_2[22]='/usr/lib/pkgconfig' SEC_MOD_2[22]=' -mc' RECURSE_2[22]=' (recurse = 0)' ##### End of PACKAGES[] package lists ##### ##### Some FILELIST[] rules below cribbed from Red Hat 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 # # If one wants to find SUID and SGID files ... # 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' # Avoids opening devices (recursion) by applying the $(Device) policy to # block and character special devices. See "select_policy" routine. # Assumed that Inode changes for listed /dev files, most under creation by udev # Note: A number of changes occur when moving from 2.6.34 to 2.6.35 kernels # - /dev/files device number changes, from "9" to "10" # - /proc file inode numbers change RULENAME[24]='Critical Devices' COMMENTS[24]='Red Hat config named kmem, mem, null, zero' FILELIST[24]="/dev/kmem /dev/mem /dev/null /dev/zero \ /dev/console /dev/cua0 /dev/initctl /dev/log \ /dev/tty[0-9] /dev/tty1[012] /dev/urandom" SEC_MOD[24]=' -i' COMMENTS_2[24]='/proc/mounts softlink undergoes time modification' FILELIST_2[24]='/proc/*' SFT_MOD_2[24]=' -mc' # Note: Stifle the temptation to use EXCEPT[]/EX_SEC[] for /lib/splash/cache # EXCEPT[] requires the listed name appear in expanded FILELIST[] wildcard # Use (recurse = 0) (or 1), rather than IGNORLST, to limit range of inspection RULENAME[25]='OS Bin and Lib Directories' FILELIST[25]='/bin /lib /sbin /usr/sbin /usr/local/sbin' ETC_SEC[25]='ReadOnly' COMMENTS_2[25]='/lib/splash/cache is an active directory' FILELIST_2[25]='/lib/splash/cache' RECURSE_2[25]=' (recurse = 0)' # Note: Inspection of /usr/lib/pkgconfig should be expanded on a system # that has a fixed or stable set of installed packages. RULENAME[26]='User Bin and Lib Directories' COMMENTS[26]= FILELIST[26]="/usr/bin /usr/local/bin /usr/local/games /usr/local/lib" SEVERITY[26]='66' ETC_SEC[26]='ReadOnly' COMMENTS_2[26]='Full recursion of /usr/lib is prolix' FILELIST_2[26]='/usr/lib' RECURSE_2[26]=' (recurse = 1)' # Note: logrotate can create false alarms # - new log file will be smaller than tripwire observation at database creation # - logrotate.conf 'nocreate' option can result in alarms at absent log files # Note: "SEC_MOD=-il" removes the "Growing" test for ALL /var/log files, which # makes the "EXCEPT[] / SEC_EX[]" example moot, as a practical matter. # It might be feasible to set observed filesize of usually growing logs # to a very small value, to enable use of "growing" check, but this moots # the point of looking for unauthorized tampering based on shrinking file. # Tripwire compares to a "fixed" reference point of day-0, and is unsuited # to detect log file tampering based on day-to-day size reduction. # Note: permissions of /var/log/rkhunter.log change during a --propupd operation # going from -rw-r--r-- to -rw------- # # Exception for log files in /var/log/*g wildcard, that don't necessarily grow RULENAME[27]='Log Files' COMMENTS[27]='`logrotate` may change logfile inodes' FILELIST[27]='/var/log/critical /var/log/messages /var/log/*g' SEVERITY[27]='66' SEC_MOD[27]=' -il' EXCEPT[27]='/var/log/rkhunter.log /var/log/Xorg.0.log' SEC_EX[27]='$(Dynamic) -i' # Note: Stifling creation of .xauth?????? files is done under xauth program # These .xauth?????? 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 permissions 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/.rnd \ /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' # Note using an incrementing variable instead of hardcoded rule number. # Using a variable for this is especially useful when adding rules from cfg file. let NEXT=${#RULENAME[@]} RULENAME[${NEXT}]='Security Control File' FILELIST[${NEXT}]='/etc/security' ETC_SEC[${NEXT}]='ReadOnly' let NEXT=${#RULENAME[@]} RULENAME[${NEXT}]='System Boot Changes' COMMENTS[${NEXT}]='Many files change inode number' FILELIST[${NEXT}]='/etc/mtab /var/run /var/run/dhcpcd /var/run/sudo' SEC_MOD[${NEXT}]=' -i' RECURSE[${NEXT}]=' (recurse = 1)' COMMENTS_2[${NEXT}]='Red Hat Policy File: Files that change when the system boots' FILELIST_2[${NEXT}]='/etc/ioctl.save /etc/.pwd.lock /var/lock/subsys' SEC_MOD_2[${NEXT}]=' -i' ########### End Default Package Lists and RuleName Definitions ########### Subroutines for Generating Policy Text Output ################################################################# # "select_policy" routine runs each filename through a gauntlet # $Filetype assignment is based on which attribute matched last. select_policy () { Filetype=Config # Default [[ $targetfile =~ ^/etc/ ]] && Filetype=Config [[ $targetfile =~ /lib/ ]] && Filetype=Lib [[ $targetfile =~ /log/ ]] && Filetype=Log [[ $targetfile =~ ^/root/ ]] && Filetype=RootFile [ -x $targetfile ] && Filetype=Bin [ -b $targetfile ] && Filetype=Block [ -c $targetfile ] && Filetype=Char [ -p $targetfile ] && Filetype=Pipe [ -S $targetfile ] && Filetype=Socket [ -d $targetfile ] && Filetype=Dir [[ "`file -b $targetfile`" =~ kernel ]] && Filetype=Kernel [[ $targetfile =~ ^/lib/modules ]] && Filetype=Kernel [ -h $targetfile ] && Filetype=SoftLink [[ $targetfile =~ ^/dev/tty ]] && Filetype=Tty [ $targetfile == "/root" ] && Filetype=RootDir [ ! -d $targetfile ] && \ [ -u $targetfile -o -g $targetfile ] && Filetype=SUID [[ "${EXCEPT[$i]}" =~ $targetfile ]] && Filetype=Special case $Filetype in Dir ) echo "-> \$(${ETC_SEC[$i]:-Dynamic})${SEC_MOD[$i]}${RECURSE[$i]} ;" ;; RootFile ) echo "-> \$(${ETC_SEC[$i]:-Dynamic})${SEC_MOD[$i]} ; # Rootfile" ;; RootDir ) echo "-> \$(IgnoreNone)-amcSH ; # Catch changes to /root" ;; Config ) echo "-> \$(${ETC_SEC[$i]:-Dynamic})${SEC_MOD[$i]} ;" ;; Kernel ) echo "-> \$(${BIN_SEC[$i]:-ReadOnly})${SEC_MOD[$i]} ; # Kernel" ;; 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 ;" ;; Char ) echo "-> \$(Device) ; # Character device" ;; Block ) echo "-> \$(Device) ; # Block device" ;; Pipe ) echo "-> \$(Device) ; # Pipe" ;; Socket ) echo "-> \$(Device) ; # Socket" ;; Special ) echo "-> ${SEC_EX[$i]} ; # Exception" ;; esac } print_header () { # if there is no config file for this script, print warning. # if there is a config file for this script, search it for a rulename assignment # if the config file has no rulename assignment, print warning. [ ! -r "$CONFIG_FILE" ] && default_rule_warning [ -r "$CONFIG_FILE" ] && \ [ -z "`grep -l RULENAME\\\[.*\\\]= $CONFIG_FILE`" ] && default_rule_warning echo " # ==================================================" echo " # Tripwire Policy File http://bugs.gentoo.org/34662" echo " # ==================================================" echo " # Generated by $0" echo " # Version $VERSION" echo " # `date '+%B %e, %Y at %R'`" echo echo ' # ============================================================================' echo ' #' echo ' # Tripwire, Inc. permission statements apply to some fully hardcoded document,' echo " # not to generated content produced by the script, ${0##*/}" echo ' #' echo ' # That said, here are the 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 ' # Tripwire is a registered trademark of Tripwire, Inc.' echo ' # (in the United States and other countries)' echo ' # All rights reserved.' echo ' #' echo ' # ============================================================================' echo echo "@@section GLOBAL" echo "HOSTNAME=\"`uname -n`\" ;" echo echo ' # Standard Tripwire File Property Mask Aliases (from `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 CFG and Data",' echo ' severity = 100' echo ')' echo '{' echo " # Tripwire file locations were transposed from ${TW_CFG}" 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 ] ======================' } 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 '# FreeBSD is a registered trademark of the FreeBSD Project Inc.' echo '# Red Hat is a registered trademark of Red Hat, Inc.' echo '#' echo '################# END of tripwire Policy Text File #################' echo } ########### Cycle through RULENAME variable arrays # "print_generated_rules" routine cycles each group of array variables through "print_a_rule" print_generated_rules () { for (( i = 0 ; i < ${#RULENAME[@]} ; i++ )) do [ "${QUIET^^}" != "YES" -a "${VERBOSE^^}" != "YES" ] && \ echo -n -e "\\r Processing rule $i of $[(10#${#RULENAME[@]}-1)] rules" >&2 print_a_rule done [ "${QUIET^^}" != "YES" ] && echo >&2 } # ------------------------------- # "print_a_rule" routine runs once for each RULENAME[] # - make a title header for the tripwire rule, including optional "emailto" field # - print ignorefiles, if any # - forward proposed package names, one-by-one, to process_packagename # - forward filelist array(s), if any, to process_filelist print_a_rule () { echo echo "# =========================================================================" echo "# RuleName: ${RULENAME[$i]}" echo "# -------------------------------------------------------------------------" [ -n "${PACKAGES[$i]}" ] && echo " Packages: ${PACKAGES[$i]}" | eval ${FOLD} | sed s/^/"# "/ [ -n "${FILELIST[$i]}" ] && echo "FileNames: ${FILELIST[$i]}" | eval ${FOLD} | 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 if [ "${SKIP_PACKAGES}" == "Yes" ]; then [ ! -z "${PACKAGES[$i]}" ] && \ echo -e "\\n# !! NOTICE !!\\n# Skipping ${RULENAME[$i]} Packages !!" else for package in ${PACKAGES[$i]} do process_packagename done fi [ -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 # - to qualify for being listed in tmp_array[], and eventually in policy text file: # - $targetfile contains "bin/", begins "/etc/" or "/var/log/", or ends ".sh" # - $targetfile is not a directory or zero-size file # - calls filename and rule printing subroutines for each targetfile in $tmp_array[] # Note: adding "lib/" results in MUCH longer list, and is not necessary because # all files in "/usr/lib" and "/usr/local/lib" directories can be watched # under a FILELIST[] rule, albeit not associated with a particular package # Note: adding the test for executables also results in a MUCH longer list process_packagename () { unset tmp_array [[ "${package}" =~ "/" ]] || package="*/${package}" for package_contents_file in `ls /var/db/pkg/${package}-[0-9]*/CONTENTS 2> /dev/null`; do tmp_array+=(` while read -a line do fname=${line[@]:1:1} case $fname in /etc/hosts ) true ;; *bin/* | /etc/* | /var/log/* | *.sh ) [ ! -d ${fname} -a -s ${fname} ] && echo $fname ;; * ) [ ! -d ${fname} -a -x ${fname} -a "${INCL_EXEC}" == "Yes" ] && echo $fname ;; esac done < ${package_contents_file}`) done if [ -n "${tmp_array}" ]; then # Empty array results in NUL output echo echo "# ${RULENAME[$i]}: $package" echo for targetfile in ${tmp_array[@]}; do output_line; select_policy done fi } # ------------------------------- # "process_filelist" routine is used only for FILELIST[] arrays # - outputs COMMENTS[], if any, for FILELIST[] array # - if file exists, calls for printing filename and tripwire policy # - blocks listing of: # - any file named "lost+found" # - any directory in the /proc/* wildcard process_filelist () { echo -e "\\n# ${RULENAME[$i]}: ${COMMENTS[$i]}\\n" for targetfile in ${FILELIST[$i]}; do case ${targetfile} in */lost+found* ) true ;; /proc/* ) if [ ! -d "$targetfile" ]; then output_line; select_policy; fi ;; * ) if [ -e "$targetfile" ]; then output_line; select_policy; fi ;; esac 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, total do echo -e -n \\t done } ########### Main Routine for Generating Policy Text Output ################################################################# print_policy_text () { print_header print_generated_rules print_footer } ########### Subroutines for the User Interface ################################################################# ########### Subroutines for Informing the User # Most messages sent to STDERR and only STDERR (>&2) # - to avoid appearing in redirected STDOUT output (the text policy) # - to avoid disappearing from view when STDOUT has been redirected default_rule_warning () { echo echo " ############# !!!! WARNING !!!! #############" echo " # Default policies may be useless on your system ... #" echo " # - the script may have overlooked critical packages #" echo " # - tripwire inspection policies may be too lax #" echo " ##########################################################" echo echo " # To view scope of tripwire inspection policies:" echo " # \`twprint -m d -d ${DB_FILE}\`" echo } recite_ver () { echo " This is ${0##*/} version $VERSION A Gentoo-oriented Tripwire Policy Text Generator " >&2 } recite_help () { recite_ver echo " Usage: ${0##*/} [-c configfile] [-u[-r][-q|-v]] [-s] [-x] [-h|-V] [debug [#]] -c Read RULENAME[], PACKAGELIST[], FILELIST[] from configfile Default (optional) configfile = $TWCFG_DIR/mktwpol.cfg -u Create tripwire policy and database after producing policy text file -r Remove policy text file after tripwire has processed it -q Quiet - stifle progress display and confirmation prompts -v Verbose - display policy text production -s Skip processing of PACKAGELIST[] arrays -x Include all executable files -h Output version and help information -V Output version information \`${0##*/}\` without \"-u\" command line parameter: - sends policy text to STDOUT, suitable for redirection with \">\" \`${0##*/} -u\` produces no policy on STDOUT. WON'T REDIRECT! - sends policy text to a file in $TWCFG_DIR - calls \`twadmin\` to create tw.pol from that file - calls \`tripwire\` to create the system database using tw.pol \`${0##*/} debug\` - limits output to one selected rule, default RULENAME[0] " >&2 exit } ########### Subroutines for Configuring the Script assign_misc_mktwpol_defaults () { # Default settings unless changed by script configuration file # FOLD="${FOLD:=fold -s -w 75}" FOLD="${FOLD:=fmt -u}" # User may change ${TW_CFG} from CONFIG_FILE # Multiple tripwire configuration locations can be maintained this way # mktwpol.sh -c mktwpol.cfg1 -> /etc/tripwire1/tw.cfg (and /etc/tripwire1/twcfg.txt) # mktwpol.sh -c mktwpol.cfg2 -> /etc/tripwire2/tw.cfg (and /etc/tripwire2/twcfg.txt) TWCFG_DIR=${TW_CFG%/*} # bash shell equivalent to the `dirname` command } # ------------------------------- # "config_mktwpol" checks for and reads a script configuration file config_mktwpol () { TWCFG_DIR=${TW_CFG%/*} CONFIG_FILE=${CONFIG_FILE:=$TWCFG_DIR/mktwpol.cfg} # Exit on "non-existence" of non-default script configuration file if [ "$CONFIG_FILE" != "$TWCFG_DIR/mktwpol.cfg" -a ! -f "$CONFIG_FILE" ]; then echo " ${0##*/} configuration file, $CONFIG_FILE, does not exist. Exiting. Goodbye. " >&2 exit 2 fi # If a script config file exists, check for the string "RULENAME[.*]=" # If the script config file purports to set a RULENAME[] (any number), and the # script config file does not say "KEEP_DEFAULT_RULES", unset the defaults if [ -f "$CONFIG_FILE" ]; then if [ -n "`grep -l RULENAME\\\[.*\\\]= $CONFIG_FILE`" -a \ -z "`grep -l KEEP_DEFAULT_RULES $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 \ EXCEPT SEC_EX fi source "$CONFIG_FILE" # if there is a CONFIG_FILE, source it fi } test_for_dupe_rules () { DUPE_PACKAGES="`echo ${PACKAGES[@]} | tr [:space:] '\n' | sort | uniq -d`" DUPE_FILELIST="`echo ${FILELIST[@]} ${FILELIST_2[@]} ${FILELIST_3[@]} ${FILELIST_4[@]} \ | tr [:space:] '\n' | sort | uniq -d`" if [ -n "${DUPE_PACKAGES}" -o -n "${DUPE_FILELIST}" ]; then echo " Uh Oh! Duplicates Found!" >&2 echo >&2 [ -n "${DUPE_PACKAGES}" ] && echo " In more than one PACKAGES[]:" ${DUPE_PACKAGES} >&2 [ -n "${DUPE_FILELIST}" ] && echo " In more than one FILELIST[]:" ${DUPE_FILELIST} >&2 echo >&2 echo " Edit $CONFIG_FILE or $0 to resolve the situation." >&2 echo " Exiting. Goodbye." >&2 exit 4 fi } # ------------------------------- # "get_twcfg_variables" reads tripwire configuration file (not the script config file) # Assuming existence of twadmin ... if tw.cfg exists, it was made by twadmin # Filename "tw.cfg" is hardcoded in tripwire, and hardcoded here get_twcfg_variables () { if [ -r ${TWCFG_DIR}/tw.cfg ]; then [ "${VERBOSE^^}" == "YES" ] && echo " Reading ${TWCFG_DIR}/tw.cfg" >&2 tmp_array=(`twadmin -m f -c ${TWCFG_DIR}/tw.cfg`) elif [ -r ${TWCFG_DIR}/twcfg.txt ]; then [ "${VERBOSE^^}" == "YES" ] && echo " Reading ${TWCFG_DIR}/twcfg.txt" >&2 tmp_array=(`cat ${TWCFG_DIR}/twcfg.txt`) else echo " ${0##*/} depends on finding a tripwire configuration file ${TWCFG_DIR}/tw.cfg is preferred ${TWCFG_DIR}/twcfg.txt is acceptable Exiting. Goodbye. " >&2 exit 3 fi # `z+=2` incrementing shaves a few thousandths of a second # but risks skipping over the required variable names for (( z = 0 ; z < ${#tmp_array[@]} ; z++ )) do case ${tmp_array[@]:$z:1} in POLFILE | DBFILE | REPORTFILE | SITEKEYFILE | LOCALKEYFILE ) assignment=${tmp_array[@]:$((z+1)):1} [ "${assignment:0:1}" == "=" ] && \ export ${tmp_array[@]:$z:1}${assignment} ;; esac done REPORTDIR=${REPORTFILE%/*} # bash shell equivalent to the `dirname` command DBDIR=${DBFILE%/*} DB_FILE=${DBFILE/\$\(HOSTNAME\)/`uname -n`} [ "${VERBOSE^^}" == "YES" ] && echo >&2 } ########### Subroutines for Operating # ------------------------------- update_tripwire_policy () { if [ "${QUIET^^}" != "YES" ]; then echo " To create the tripwire policy file (${POLFILE}), and tripwire database (in ${DBDIR}/), run: twadmin --create-polfile $TRIPWIRE_POL tripwire --init " >&2 echo -n " Create tripwire policy and database now? [Y/n]: " >&2 read -n 1 -t 15 RUN_TRIPWIRE echo >&2 fi if [ "${RUN_TRIPWIRE^}" == "N" ]; then echo " Skipping creation of tripwire policy and database. Goodbye. " >&2 exit fi twadmin --create-polfile $TRIPWIRE_POL if [ "${REMOVE_POL}" == "Yes" ]; then if [ "${QUIET^^}" != "YES" ]; then echo -n " Delete $TRIPWIRE_POL now? [Y/n]: " >&2 read -n 1 -t 15 YES_IM_SURE echo >&2 fi if [ "${YES_IM_SURE^}" != "N" ]; then rm -f $TRIPWIRE_POL fi fi tripwire --init } # ------------------------------- mode_auto_update () { TRIPWIRE_POL=$TWCFG_DIR/twpol-`date +%y%m%d-%H%M`.txt if [ "${VERBOSE^^}" == "YES" ]; then print_policy_text | tee $TRIPWIRE_POL elif [ "${QUIET^^}" != "YES" ]; then echo " Writing to $TRIPWIRE_POL ..." >&2 fi if [ "${VERBOSE^^}" != "YES" ]; then print_policy_text > $TRIPWIRE_POL [ "${QUIET^^}" != "YES" ] && echo >&2 fi update_tripwire_policy } # ------------------------------- mode_echo_policy () { if [ "${QUIET^^}" != "YES" ]; then echo " Run \`${0##*/} -h\` for tips on use. Creating tripwire policy text ... " >&2 fi print_policy_text } # ------------------------------- mode_debug () { DEBUGME=y # Unused variable. Can be useful for internal debugging. i=${1:-0} # User can debug any single rule, default RULENAME[0] 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 1 } ########### Main Routine ################################################################# # Default RULENAME[] arrays and TW_CFG variable are in memory # Process command line parameters while getopts :c:uqvrsxhV OPTION do case $OPTION in c ) CONFIG_FILE=$OPTARG ;; u ) UPDATETW=Yes ;; q ) QUIET=Yes ;; v ) VERBOSE=Yes ;; r ) REMOVE_POL=Yes ;; s ) SKIP_PACKAGES=Yes ;; x ) INCL_EXEC=Yes ;; h ) config_mktwpol; recite_help ;; V ) recite_ver; exit ;; * ) config_mktwpol; recite_help ;; esac done shift $(($OPTIND - 1)) config_mktwpol test_for_dupe_rules assign_misc_mktwpol_defaults get_twcfg_variables [ "$1" == "debug" ] && mode_debug $2 if [ "$UPDATETW" == "Yes" ]; then mode_auto_update else mode_echo_policy fi