pf-badhost 0.5 errata 001, January 25, 2021: This is a hotfix release to address issues related to shell portability, RFC3330/5156 filtering and to also improve support for running pf-badhost on custom OS types. Issues fixed: * RFC3330 and/or RFC5156 addresses could sneak into the blockist when using subnet aggregation - we now explicitely whitelist these addresses. * Explicitly specify stdin as input for myawk, mygrep and mysort functions as their stdin autodetection could get confused when invoked from certain shells. * Tweak the pre-execution tests to make using a custom OS easier, including generating lists with '-x' on machines without PF. * Make '-h' switch slightly more useful for new users. Apply by doing: (Note: Provided file path is for OpenBSD, other platforms path may differ) patch /usr/local/bin/pf-badhost < p0.patch --- pf-badhost.sh.orig0 Sun Jan 10 21:49:10 2021 +++ pf-badhost.sh Sun Jan 24 12:25:28 2021 @@ -36,7 +36,7 @@ # If adding your own IPv6 lists, the addresses must be RFC 5952 compliant and # have one address per line with no leading or trailing whitespace. -version='0.5' +version='0.5p0' release_date='2021-01-10' release_name='Psychological Operations' @@ -281,7 +281,7 @@ # in RFC3330 & RFC5156 unless manually specified as a custom rule. # To disable this behavior, set the 2 options below to '0' _RFC3330=1 - _RFC3330=1 + _RFC5156=1 # # Manual filtering and whitelisting: # It usually makes more sense to negate an address using a custom @@ -353,7 +353,9 @@ printf '# Supported Operating Systems:\n#\n# * OpenBSD\n# * FreeBSD\n# * NetBSD\n# * DragonflyBSD\n# * MacOS\n#\n' printf '# OS Type Can Be Specified As An Argument:\n' printf '# Example: "pf-badhost -O DragonflyBSD"\n#\n' - printf '# NOTE: OS arguments are case insensitive\n' + printf '# NOTE: OS arguments are case insensitive\n#\n' + printf '# The man page can be found at:\n' + printf '# https://geoghegan.ca/pub/pf-badhost/0.5/man/man.txt\n' printf '###################################################################\n\n' } @@ -404,11 +406,11 @@ # Opportunistically use mawk or GNU awk if they're available myawk() { if command -v mawk >/dev/null 2>&1 ; then - nice mawk "$@" + nice mawk "$@" - elif command -v gawk >/dev/null 2>&1 ; then - nice gawk "$@" + nice gawk "$@" - else - nice awk "$@" + nice awk "$@" - fi } @@ -428,20 +430,20 @@ # Opportunistically use RipGrep or GNU grep if they're available mygrep() { if command -v rg >/dev/null 2>&1 ; then - nice rg "$@" || true + nice rg "$@" - || true elif command -v ggrep >/dev/null 2>&1 ; then - nice ggrep -E "$@" || true + nice ggrep -E "$@" - || true else - nice grep -E "$@" || true + nice grep -E "$@" - || true fi } # Opportunistically use GNU sort if available (needed for NetBSD support) mysort() { if command -v gsort >/dev/null 2>&1 ; then - nice gsort "$@" + nice gsort "$@" - else - nice sort "$@" + nice sort "$@" - fi } @@ -566,7 +568,6 @@ LIST_GEN() { # Make sure there are no empty files in listdir - typeset _file find "${listdir}" -type f -size 0 -delete || WARN_ERR 'ERROR: Failed to delete temporary files!' # Filter and Generate IP Address List @@ -667,6 +668,22 @@ printf '\n# Tor Blacklist:\n\n' cat -- < "${tor_blacklist}" fi + # System rules (RFC3330/5156 negation) + printf '\n# System Rules:\n\n' + if [ "${_RFC3330}" -ne 1 ] && [ "${_RFC5156}" -ne 1 ]; then + # Allow all private address ranges in blocklist (may break stuff) + true + elif [ "${_RFC3330}" -eq 1 ] && [ "${_RFC5156}" -ne 1 ]; then + # Allow RFC5156 addresses in blocklist, filter out RFC3330 addresses + printf '%s\n' "${_rfc3330[@]}" + elif [ "${_RFC3330}" -ne 1 ] && [ "${_RFC5156}" -eq 1 ]; then + # Allow RFC3330 addresses in blocklist, filter out RFC5156 addresses + printf '%s\n' "${_rfc5156[@]}" + else + # Make sure RFC 3330 & 5156 address ranges are not in blocklist + printf '%s\n' "${_rfc3330[@]}" + printf '%s\n' "${_rfc5156[@]}" + fi # Main ruleset if [ "${_IPV4}" -eq 1 ]; then printf '\n# IPv4 List Generated Rules:\n\n' @@ -967,15 +984,18 @@ fi # Make sure requisite utilities are installed - for _cmd in 'cmp' 'find' 'gunzip' 'nc' 'pfctl' ; do + for _cmd in 'cmp' 'find' 'gunzip' 'nc' "${netget}" ; do CHECK_CMD "${_cmd}" done > /dev/null if [ "${_NO_UID_CHECK}" -ne 1 ]; then CHECK_PRIVILEGE fi - + if [ "${_HAIL_MARY}" -eq 1 ]; then + CHECK_CMD "${authlog_unzip}" > /dev/null + fi if [ "${_PRINT_ONLY}" -ne 1 ]; then + pfctl="$(CHECK_CMD pfctl)" CHECK_DRIVE fi @@ -1030,11 +1050,6 @@ elif [ "${_TOR_BLOCK_ALL}" -eq 1 ] && [ "${_TOR_BLOCK_EXIT}" -eq 1 ]; then ERR 'Tor Whitelisting/Blacklisting options are mutually exclusive!' fi - - # Make sure user provided OS is accurate and that specified utilities exist - for _cmd in "${getroot}" "${netget}" "${authlog_unzip}" ; do - command -v -- "${_cmd}" >/dev/null 2>&1 || ERR "'${_cmd}' not found! Did you specify the correct OS type?" - done } # ------------------------------------------------------------------------------ @@ -1145,17 +1160,46 @@ # Set trap handler trap TRAP_ABORT ERR INT - # Registrar URL Array (this is declared late because zsh needs ksh array syntax enabled before it can ingest array data) + # These are declared late because zsh needs ksh array syntax enabled before it can ingest array data _registrar_url[0]='https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest' _registrar_url[1]='https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest' _registrar_url[2]='https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest' _registrar_url[3]='https://ftp.apnic.net/stats/apnic/delegated-apnic-latest' _registrar_url[4]='https://ftp.ripe.net/ripe/stats/delegated-ripencc-latest' + _rfc3330[0]='!0.0.0.0/8' + _rfc3330[1]='!10.0.0.0/8' + _rfc3330[2]='!14.0.0.0/8' + _rfc3330[3]='!24.0.0.0/8' + _rfc3330[4]='!39.0.0.0/8' + _rfc3330[5]='!127.0.0.0/8' + _rfc3330[6]='!128.0.0.0/16' + _rfc3330[7]='!169.254.0.0/16' + _rfc3330[8]='!172.16.0.0/12' + _rfc3330[9]='!191.255.0.0/16' + _rfc3330[10]='!192.0.0.0/24' + _rfc3330[11]='!192.0.2.0/24' + _rfc3330[12]='!192.88.99.0/24' + _rfc3330[14]='!192.168.0.0/16' + _rfc3330[14]='!198.18.0.0/15' + _rfc3330[15]='!223.255.255.0/24' + _rfc3330[16]='!224.0.0.0/3' + + _rfc5156[0]='!2001:10::/28' + _rfc5156[1]='!2001::/32' + _rfc5156[2]='!2001:db8::/32' + _rfc5156[3]='!2002::/16' + _rfc5156[4]='!3ffe::/16' + _rfc5156[5]='!5f00::/8' + _rfc5156[6]='!::FFFF:0:0/96' + _rfc5156[7]='!fc00::/7' + _rfc5156[8]='!fe80::/10' + _rfc5156[9]='!ff00::/8' + # Mark program info read-only readonly version release_date release_name # Mark pf-badhost constants as read-only - readonly _registrar_url _SUBNET_MERGE_PERL + readonly _rfc3330 _rfc5156 _registrar_url _SUBNET_MERGE_PERL # Mark user-defined lists as read-only readonly _COUNTRY_CODES _ASN_LIST _BLOCKLISTS _USER_RULES @@ -1167,7 +1211,7 @@ # Initialize local vars typeset _i # Initialize global configuration vars - _CHECK_ONLY=0 ; _NO_UID_CHECK=0 ; _PRINT_ONLY=0 ; _VERBOSE=1 ; pfctl="$(CHECK_CMD pfctl)" + _CHECK_ONLY=0 ; _NO_UID_CHECK=0 ; _PRINT_ONLY=0 ; _VERBOSE=1 # Command-line option handling while getopts 46ABDE:F:GH:J:K:O:R:T:VZ:a:b:g:hj:l:no:r:u:w:x _opts ; do @@ -1274,7 +1318,7 @@ else ERR "File '${OPTARG}' either not found or has incorrect permissions!" fi ;; - x) _PRINT_ONLY=1 ; _LOG=0 ; _NO_UID_CHECK=1 ;; # Print generated list to stdout + x) _PRINT_ONLY=1 ; _LOG=0 ; _NO_UID_CHECK=1 ; confpath='/dev/null' ; getroot='false' ;; # Print generated list to stdout ?) HELP_MESSAGE 1>&2 ; exit 2 ;; esac done @@ -1331,9 +1375,11 @@ custom) test -n "${getroot}" || ERR "Custom OS type specified - please set doas/sudo preference with '-Z' option" test -n "${netget}" || ERR "Custom OS type specified - please set curl/fetch/ftp/wget preference with '-F' option" - test -n "${authlog_path1}" || ERR "Custom OS type specified - please specifiy path to SSH authlog with '-J' option" - test -n "${authlog_path2}" || ERR "Custom OS type specified - please specifiy path to secondary SSH authlog with '-K' option" - test -n "${authlog_unzip}" || ERR "Custom OS type specified - please specifiy zcat/bzcat for SSH authlog analysis with '-E' option" + if [ "${_HAIL_MARY}" -eq 1 ]; then + test -n "${authlog_path1}" || ERR "Custom OS type specified - please specifiy path to SSH authlog with '-J' option" + test -n "${authlog_path2}" || ERR "Custom OS type specified - please specifiy path to secondary SSH authlog with '-K' option" + test -n "${authlog_unzip}" || ERR "Custom OS type specified - please specifiy zcat/bzcat for SSH authlog analysis with '-E' option" + fi ;; *) printf '\n\nUnknown Operating System Specified. Available Options Are:\n * -OpenBSD\n * -FreeBSD\n * -NetBSD\n * -DragonflyBSD\n * -MacOS\n\nQuitting Without Making Changes...\n\n' @@ -1342,7 +1388,7 @@ esac # Mark operating system specific variables as read-only - readonly getroot netget authlog_path1 authlog_path2 authlog_unzip pfctl + readonly getroot netget authlog_path1 authlog_path2 authlog_unzip # Config test / dry run if [ "${_CHECK_ONLY}" -eq 1 ]; then