rlm@1: #!/bin/bash rlm@1: # Copyright (c) 2004 Matthias S. Benkmann
rlm@1: # You may do everything with this code except misrepresent its origin. rlm@1: # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND! rlm@1: rlm@1: if [ $# -lt 1 -o \( $# -gt 1 -a "z$1" != "z:man" -a "z$1" != "z:mani" -a "z$1" != "z:lib" \) -o "$1" = "--help" ]; then rlm@1: echo 1>&2 rlm@1: echo 1>&2 'USAGE: '"${0##*/}"' ' rlm@1: echo 1>&2 rlm@1: echo 1>&2 ' Entries will be matched if group and/or user equals ' rlm@1: echo 1>&2 ' (numeric UID/GID allowed).' rlm@1: echo 1>&2 ' This script uses `forall_direntries_from'"'"' and `list_suspicious_files_from'"'." rlm@1: echo 1>&2 rlm@1: echo 1>&2 ' NOTE: Several minutes may pass before you see the first output.' rlm@1: echo 1>&2 ' You should probably redirect output to a file for later reference.' rlm@1: echo 1>&2 rlm@1: echo 1>&2 ' WARNING! This program is for listing files from package users only!' rlm@1: echo 1>&2 ' Do NOT use it to list files from untrusted users!' rlm@1: echo 1>&2 ' An untrusted user could set up a manipulated manpage to exploit' rlm@1: echo 1>&2 ' a bug in man when it is used to extract the summary!' rlm@1: exit 1 rlm@1: fi rlm@1: rlm@1: # KNOWN BUGS: rlm@1: # - when extracting summaries from manpages, candidate manpages are considered rlm@1: # in alphabetic order rather than the order used by the man command. rlm@1: # The problem with this is that section 8, which contains manpages for rlm@1: # admin commands, will be considered after lower-numbered sections. rlm@1: # In the rare case that an admin command has the same name as a topic from rlm@1: # a lower-numbered manpage installed by the same package, the summary will rlm@1: # be taken from the wrong manpage. rlm@1: # An example for such a clash are the faillog.5 and faillog.8 manpages from rlm@1: # the shadow package. rlm@1: # Because this problem is difficult to fix, rare and easily spotted (since rlm@1: # the manpage that should have provided the summary will be listed under rlm@1: # EXTRA MANPAGES) I won't fix it. rlm@1: rlm@1: ugname="$1" rlm@1: rlm@1: #suppress ugly debug output from shell rlm@1: trap ':' SIGPIPE rlm@1: rlm@1: if [ $# -gt 1 ]; then rlm@1: name="${2##*/}" rlm@1: case "$1" in rlm@1: :man) rlm@1: name=${name%.gz} rlm@1: name=${name%.bz2} rlm@1: name=${name%.*} rlm@1: echo $'command\2'"$name"$'\2man\2'"$2" ;; rlm@1: :mani) rlm@1: name=${name%.gz} rlm@1: name=${name%.bz2} rlm@1: name=${name%.*} rlm@1: echo $'command\2'"$name"$'\2mani\2'"$2" ;; rlm@1: :lib) rlm@1: name=${name%.a} rlm@1: name=${name%%.so*} rlm@1: echo "lib $name" rlm@1: ;; rlm@1: esac rlm@1: exit 0 rlm@1: fi rlm@1: rlm@1: sanitize() { tr -c '[:print:]' '?' ; } rlm@1: rlm@1: # $1: rlm@1: # $2: command\2\2cmd\2(->) rlm@1: # $3: command\2\2man[i]\2 or rlm@1: expand_command() rlm@1: { rlm@1: sep=$'\2' rlm@1: cmdname="$1" rlm@1: cmdline="$2" rlm@1: manline="$3" rlm@1: linktarget="${cmdline##*${sep}}" rlm@1: rlm@1: if [ -z "$manline" ]; then rlm@1: description='No manpage' rlm@1: #the "l" at the beginning is just to make it sort after "lib" rlm@1: echo -n "lmanlessbin $cmdname" | sanitize rlm@1: echo rlm@1: rlm@1: else # if [ ! -z "$manline" ]; then rlm@1: manpage=${manline##*${sep}} rlm@1: manpagedir=${manpage%/*} rlm@1: wsc='[[:space:],]\+' rlm@1: # The cd $manpagedir is a workaround for a bug in man-db that causes it rlm@1: # to attempt to resolve paths relative to cwd. rlm@1: # The `t l;d;:l;n;b l' in the sed command is voodoo magic to make sed rlm@1: # output only the first match but to keep eating up all input. I use this rlm@1: # instead of `| head -n 1', because head breaks the pipe after doing rlm@1: # its 1 line output, which (if it happens before sed has processed the rlm@1: # complete input) freaks out man and causes it to emit a totally rlm@1: # silly error message including "No such file or directory", which is rlm@1: # annoying when you do testing without suppressing man's errors. rlm@1: # The $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' removes the backspace-based rlm@1: # as well as ESC-based formatting from man's output. rlm@1: description="$( { cd "$manpagedir/.." 2>/dev/null ; rlm@1: COLUMNS=300 man "$manpage" 2>/dev/null || rlm@1: echo " $name - Broken manpage" ; } | rlm@1: sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' | rlm@1: sed -n "/^NAME/,/^[A-Z]/s/^.*${wsc}${cmdname}${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )" rlm@1: if [ -z "$description" ]; then rlm@1: description="$( cd "$manpagedir/.." 2>/dev/null ; rlm@1: COLUMNS=300 man "$manpage" 2>/dev/null | rlm@1: sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' | rlm@1: sed -n "s/^.*${wsc}..*${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )" rlm@1: fi rlm@1: test -z "$description" && description="Weird manpage" rlm@1: fi rlm@1: rlm@1: echo -n "binexe $cmdname" | sanitize rlm@1: test "$linktarget" != '(->)' && echo -n "$linktarget" | sanitize rlm@1: echo rlm@1: #the "lx" in "lxdescription" is just to make sure it sorts after "lmanlessbin" rlm@1: echo -n "lxdescription $cmdname: $description" | sanitize rlm@1: echo rlm@1: } rlm@1: rlm@1: # NOTE: The -path and -lname stuff at the beginning of the following is rlm@1: # there to make sure that none of the lines output by find contains rlm@1: # a) \n or \r, because that would mess up post-processing the output rlm@1: # line-by-line. rlm@1: # b) \x7f, because this character triggers one of the nastier bash-bugs rlm@1: # wrt string handling rlm@1: # c) \2, because I use this as separator within the lines rlm@1: # (Why \2 and not \0 or \1 ? Because bash can't cope with \0 at all and has rlm@1: # bugs related to \1.) rlm@1: # rlm@1: # Because of this, having the final section called "ALL FILES" is technically rlm@1: # a lie, because files with a path containing one of the abovementioned rlm@1: # characters will not appear in output. rlm@1: # However, a) no sane package contains such files rlm@1: # b) they will be listed in the output from list_suspicious_files rlm@1: cmd=(\( -path $'*\n*' -or -path $'*\r*' -or -path $'*\x7f*' rlm@1: -or -path $'*\2*' rlm@1: -or -lname $'*\n*' -or -lname $'*\r*' -or -lname $'*\x7f*' rlm@1: -or -lname $'*\2*' rlm@1: \) rlm@1: -or rlm@1: \( rlm@1: \( -printf "zall %p\n" \) , rlm@1: \( rlm@1: \( -type f -or -xtype f \) -and rlm@1: \( rlm@1: \( -perm -u+x \( -path "*/bin/*" -or -path "*/sbin/*" \) -printf 'command\2%f\2cmd\2(->%l)\n' \) rlm@1: -or \( -path "*/man/man*/*" -exec "$0" ":man" {} \; \) rlm@1: -or \( -path "*/man/*/man*/*" -exec "$0" ":mani" {} \; \) rlm@1: -or \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" -exec "$0" ":lib" {} \; \) rlm@1: -or \( -type f -perm -u+x -not \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" \) -printf "nobinexe %p\n" \) rlm@1: \) rlm@1: \) rlm@1: \) rlm@1: ) rlm@1: rlm@1: forall_direntries_from "$ugname" "${cmd[@]}" | sort -u | rlm@1: { rlm@1: sep=$'\2' rlm@1: hold='' rlm@1: for((;;)) rlm@1: do rlm@1: if [ -z "$hold" ]; then rlm@1: read -r line || break rlm@1: else rlm@1: line="$hold" rlm@1: hold='' rlm@1: fi rlm@1: rlm@1: case "z$line" in rlm@1: zcommand${sep}*${sep}cmd${sep}*) rlm@1: cmdname=${line#command${sep}} rlm@1: cmdname=${cmdname%%${sep}*} rlm@1: read -r hold rlm@1: case "z$hold" in rlm@1: zcommand${sep}${cmdname}${sep}man${sep}*|zcommand${sep}${cmdname}${sep}mani${sep}*) rlm@1: expand_command "$cmdname" "$line" "$hold" rlm@1: hold='' rlm@1: ;; rlm@1: rlm@1: z*) rlm@1: expand_command "$cmdname" "$line" "" rlm@1: ;; rlm@1: esac rlm@1: ;; rlm@1: rlm@1: zcommand${sep}*${sep}man${sep}*|command${sep}*${sep}mani${sep}*) rlm@1: rlm@1: echo -n "manextra ${line##*${sep}}" | sanitize rlm@1: echo rlm@1: ;; rlm@1: rlm@1: z*) rlm@1: echo -n "$line" | sanitize rlm@1: echo rlm@1: ;; rlm@1: esac rlm@1: rlm@1: done rlm@1: } | sort | #no -u here, bc. the above processing may equalize different files rlm@1: { rlm@1: # (1) binexe: Executables (in *bin/) rlm@1: # (2) lib: Libraries (in *lib/*) rlm@1: # (3) lmanlessbin: Executables (in *bin/) without manpages rlm@1: # (4) lxdescription: Summaries for executables (in *bin/) rlm@1: # (5) manextra: Extra manpages rlm@1: # full paths, no perms rlm@1: # (6) nobinexe: Executables not in *bin/ (excluding *lib/*.so and *lib/*.so.*) rlm@1: # full paths, no perms rlm@1: # (7) zall: All files rlm@1: # full paths, no perms rlm@1: rlm@1: curstate='' rlm@1: while read -r line rlm@1: do rlm@1: newstate="${line%% *}" rlm@1: if [ "$newstate" != "$curstate" ]; then rlm@1: curstate="$newstate" rlm@1: case "$curstate" in rlm@1: binexe) rlm@1: echo 'EXECUTABLES (in */bin or */sbin)' rlm@1: echo -n " ${line#binexe }" rlm@1: ;; rlm@1: lib) rlm@1: echo rlm@1: echo rlm@1: echo 'LIBRARIES (lib*.a or lib*.so)' rlm@1: echo -n " ${line#lib }" rlm@1: ;; rlm@1: lmanlessbin) rlm@1: echo rlm@1: echo rlm@1: echo 'EXECUTABLES WITH NO MANPAGE (in */bin or */sbin)' rlm@1: echo -n " ${line#lmanlessbin }" rlm@1: ;; rlm@1: lxdescription) rlm@1: echo rlm@1: echo rlm@1: echo 'MANPAGE SUMMARIES OF EXECUTABLES (in */bin or */sbin)' rlm@1: echo " ${line#lxdescription }" rlm@1: ;; rlm@1: manextra) rlm@1: echo rlm@1: echo 'EXTRA MANPAGES' rlm@1: echo " ${line#manextra }" rlm@1: ;; rlm@1: nobinexe) rlm@1: echo rlm@1: echo 'EXTRA EXECUTABLES (not in */bin or */sbin)' rlm@1: echo " ${line#nobinexe }" rlm@1: ;; rlm@1: zall) rlm@1: echo rlm@1: echo 'ALL FILES' rlm@1: echo " ${line#zall }" rlm@1: ;; rlm@1: *) rlm@1: echo rlm@1: echo rlm@1: echo 'UNEXPECTED LINE' rlm@1: echo " $line" rlm@1: ;; rlm@1: rlm@1: esac rlm@1: else rlm@1: case "$curstate" in rlm@1: binexe) echo -n ", ${line#binexe }" rlm@1: ;; rlm@1: lib) echo -n ", ${line#lib }" rlm@1: ;; rlm@1: lmanlessbin) echo -n ", ${line#lmanlessbin }" rlm@1: ;; rlm@1: lxdescription) echo " ${line#lxdescription }" rlm@1: ;; rlm@1: manextra) echo " ${line#manextra }" rlm@1: ;; rlm@1: nobinexe) echo " ${line#nobinexe }" rlm@1: ;; rlm@1: zall) echo " ${line#zall }" rlm@1: ;; rlm@1: *) rlm@1: echo rlm@1: echo 'UNEXPECTED LINE' rlm@1: echo " $line" rlm@1: ;; rlm@1: esac rlm@1: fi rlm@1: done rlm@1: } rlm@1: rlm@1: list_suspicious_files_from "$ugname"