Summary
I wrote a quick mtree HIDS script to supplement the concept I alluded to in this post.
As a HIDS, this is nowhere near as flexible as some of the better third-party apps (e.g. AIDE), but it does offer the following benefits:
Do not treat this alone as a comprehensive HIDS solution. A proper implementation would include facilities to keep the HIDS db on removable media and/or to encrypt and/or sign the HIDS db files.
(In my case, I am using this from a host system to check integrity on certain jail directories.)
Usage
Be sure to manually create /usr/local/mtreedb/dirlist.txt. I recommend changing /usr/local/mtreedb ownership to root, and settings its mode with the octal 0700.
Example dirlist.txt (used by autoinit & autocheck)
The script
Preliminary test cases look good. If I discover any bugs I'll post a patch here.
I wrote a quick mtree HIDS script to supplement the concept I alluded to in this post.
As a HIDS, this is nowhere near as flexible as some of the better third-party apps (e.g. AIDE), but it does offer the following benefits:
- It uses only tools in the FreeBSD base system (sh, mtree, etc.).
- It allows you to check and (re-)initialize just part (i.e. a single directory) of your HIDS db, or the entire thing, as needed.
- It's quick to set up and easy to use.
- When the autocheck is run as a cronjob, it will not send you needless chatter. It only makes noise when a difference is discovered.
Do not treat this alone as a comprehensive HIDS solution. A proper implementation would include facilities to keep the HIDS db on removable media and/or to encrypt and/or sign the HIDS db files.
(In my case, I am using this from a host system to check integrity on certain jail directories.)
Usage
Code:
# ./mtree-hids.sh help
Usage: ./mtree-hids.sh help
./mtree-hids.sh (init|check) directory
./mtree-hids.sh (autoinit|autocheck)
----------------------
help : prints this message
init : creates mtree database for a single directory
check : checks a single directory against its mtree database
autoinit : creates mtree databases for all directories
in /usr/local/mtreedb/dirlist.txt
autocheck : checks all directories in /usr/local/mtreedb/dirlist.txt
against their mtree databases
Be sure to manually create /usr/local/mtreedb/dirlist.txt. I recommend changing /usr/local/mtreedb ownership to root, and settings its mode with the octal 0700.
Example dirlist.txt (used by autoinit & autocheck)
Code:
# use absolute pathnames
/bin
/usr/bin
/usr/local/bin
/sbin
/usr/sbin
/usr/local/sbin
/lib
/usr/lib
/usr/local/lib
/libexec
/usr/libexec
/usr/local/libexec
/etc
/usr/local/etc
# add any others...
The script
Code:
#!/bin/sh
# Author: anomie
# Date : 2009-09-15
# Please see copyright at bottom of script
PATH=/bin:/usr/bin:/sbin:/usr/sbin
# ---------------------------------------------------------------------------
# Variable assignments
# ---------------------------------------------------------------------------
_keywords='uid,gid,mode,sha1digest,flags' # see mtree(8) manpages
_dbpath='/usr/local/mtreedb'
_dirlist='/usr/local/mtreedb/dirlist.txt'
# ---------------------------------------------------------------------------
# Functions
# ---------------------------------------------------------------------------
print_usage() {
echo "Usage: ${0} help"
echo " ${0} (init|check) directory"
echo " ${0} (autoinit|autocheck)"
}
print_help() {
echo "----------------------"
echo " help : prints this message"
echo " init : creates mtree database for a single directory"
echo " check : checks a single directory against its mtree database"
echo " autoinit : creates mtree databases for all directories"
echo " in ${_dirlist}"
echo " autocheck : checks all directories in ${_dirlist}"
echo " against their mtree databases"
}
audit_and_name_db() {
if [ -z "${_dir}" ] ; then
print_usage
exit 1
fi
if [ ! -d "${_dir}" ] ; then
echo "${0}: there is no directory ${_dir}"
exit 1
fi
#
# Give our mtree db file a name
#
_db="${_dbpath}/db-$(echo ${_dir} | tr '/' '_')"
}
audit_auto() {
if [ ! -f "${_dirlist}" ] ; then
echo "${0}: ${_dirlist} does not exist."
echo "Auto mode can not work without it."
exit 1
fi
if [ ! -s "${_dirlist}" ] ; then
echo "${0}: ${_dirlist} is empty."
echo "Nothing to do here."
exit 1
fi
}
create_db() {
echo "Creating new mtree db file for ${_dir}...."
mtree -p ${_dir} -k ${_keywords} -c > ${_db}
if [ ${?} -ne 0 ] ; then
echo "${0}: problem creating mtree db file for ${_dir}."
exit 1
fi
}
check_integrity() {
#
# Check directory integrity against existing mtree database
#
if [ ! -e "${_db}" ] ; then
echo "${0}: no mtree db found at ${_db}."
echo "Check your path, or init a new db file."
exit 1
fi
mtree -p ${_dir} -f ${_db} > ${_db}.tmp.output
_rc=${?}
if [ ${_rc} -eq 2 ] || [ -s "${_db}.tmp.output" ] ; then
echo '--------'
echo "WARNING: mtree found differences for ${_dir}"
echo '--------'
cat ${_db}.tmp.output
rm -f ${_db}.tmp.output
elif [ ${_rc} -ne 0 ] ; then
echo "${0}: error checking integrity of ${_dir}."
echo "(mtree exited with: ${_rc})"
exit 1
fi
#
# Clean up the empty file
#
rm -f ${_db}.tmp.output
}
create_db_auto() {
#
# Note that blank lines and comments (#) are silently ignored
#
for I in $(cat ${_dirlist} | egrep -v '^$|^#') ; do
_dir=${I}
audit_and_name_db
create_db
done
}
check_integrity_auto() {
#
# Note that blank lines and comments (#) are silently ignored
#
for I in $(cat ${_dirlist} | egrep -v '^$|^#') ; do
_dir=${I}
audit_and_name_db
check_integrity
done
}
# ---------------------------------------------------------------------------
# Main logic
# ---------------------------------------------------------------------------
#
# This first decision structure handles audits and some variable
# initialization
#
case "${1}" in
"help") print_usage
print_help
exit 0
;;
"init"|"check")
_dir="${2}"
audit_and_name_db
;;
"autoinit"|"autocheck")
audit_auto
;;
*) print_usage
exit 1
;;
esac
#
# This second decision structure does the actual work
#
case "${1}" in
"init") create_db
;;
"check") check_integrity
;;
"autoinit") create_db_auto
;;
"autocheck") check_integrity_auto
;;
*) exit 99 # how did you get here?
;;
esac
exit 0
# ---------------------------------
# Copyright (c) 2009 anomie
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
Preliminary test cases look good. If I discover any bugs I'll post a patch here.