annotate previous-work/more_control_helpers/bin/list_package @ 20:c75924bd38e3

fix security hole relating to path for package user.
author Robert McIntyre <rlm@mit.edu>
date Thu, 10 Jan 2013 04:25:17 +0000
parents d6bef198ae71
children
rev   line source
rlm@1 1 #!/bin/bash
rlm@1 2 # Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
rlm@1 3 # You may do everything with this code except misrepresent its origin.
rlm@1 4 # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
rlm@1 5
rlm@1 6 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 7 echo 1>&2
rlm@1 8 echo 1>&2 'USAGE: '"${0##*/}"' <user_or_group_name>'
rlm@1 9 echo 1>&2
rlm@1 10 echo 1>&2 ' Entries will be matched if group and/or user equals <user_or_group_name>'
rlm@1 11 echo 1>&2 ' (numeric UID/GID allowed).'
rlm@1 12 echo 1>&2 ' This script uses `forall_direntries_from'"'"' and `list_suspicious_files_from'"'."
rlm@1 13 echo 1>&2
rlm@1 14 echo 1>&2 ' NOTE: Several minutes may pass before you see the first output.'
rlm@1 15 echo 1>&2 ' You should probably redirect output to a file for later reference.'
rlm@1 16 echo 1>&2
rlm@1 17 echo 1>&2 ' WARNING! This program is for listing files from package users only!'
rlm@1 18 echo 1>&2 ' Do NOT use it to list files from untrusted users!'
rlm@1 19 echo 1>&2 ' An untrusted user could set up a manipulated manpage to exploit'
rlm@1 20 echo 1>&2 ' a bug in man when it is used to extract the summary!'
rlm@1 21 exit 1
rlm@1 22 fi
rlm@1 23
rlm@1 24 # KNOWN BUGS:
rlm@1 25 # - when extracting summaries from manpages, candidate manpages are considered
rlm@1 26 # in alphabetic order rather than the order used by the man command.
rlm@1 27 # The problem with this is that section 8, which contains manpages for
rlm@1 28 # admin commands, will be considered after lower-numbered sections.
rlm@1 29 # In the rare case that an admin command has the same name as a topic from
rlm@1 30 # a lower-numbered manpage installed by the same package, the summary will
rlm@1 31 # be taken from the wrong manpage.
rlm@1 32 # An example for such a clash are the faillog.5 and faillog.8 manpages from
rlm@1 33 # the shadow package.
rlm@1 34 # Because this problem is difficult to fix, rare and easily spotted (since
rlm@1 35 # the manpage that should have provided the summary will be listed under
rlm@1 36 # EXTRA MANPAGES) I won't fix it.
rlm@1 37
rlm@1 38 ugname="$1"
rlm@1 39
rlm@1 40 #suppress ugly debug output from shell
rlm@1 41 trap ':' SIGPIPE
rlm@1 42
rlm@1 43 if [ $# -gt 1 ]; then
rlm@1 44 name="${2##*/}"
rlm@1 45 case "$1" in
rlm@1 46 :man)
rlm@1 47 name=${name%.gz}
rlm@1 48 name=${name%.bz2}
rlm@1 49 name=${name%.*}
rlm@1 50 echo $'command\2'"$name"$'\2man\2'"$2" ;;
rlm@1 51 :mani)
rlm@1 52 name=${name%.gz}
rlm@1 53 name=${name%.bz2}
rlm@1 54 name=${name%.*}
rlm@1 55 echo $'command\2'"$name"$'\2mani\2'"$2" ;;
rlm@1 56 :lib)
rlm@1 57 name=${name%.a}
rlm@1 58 name=${name%%.so*}
rlm@1 59 echo "lib $name"
rlm@1 60 ;;
rlm@1 61 esac
rlm@1 62 exit 0
rlm@1 63 fi
rlm@1 64
rlm@1 65 sanitize() { tr -c '[:print:]' '?' ; }
rlm@1 66
rlm@1 67 # $1: <commandname>
rlm@1 68 # $2: command\2<commandname>\2cmd\2(-><linktarget>)
rlm@1 69 # $3: command\2<commandname>\2man[i]\2<manpage_path> or <empty>
rlm@1 70 expand_command()
rlm@1 71 {
rlm@1 72 sep=$'\2'
rlm@1 73 cmdname="$1"
rlm@1 74 cmdline="$2"
rlm@1 75 manline="$3"
rlm@1 76 linktarget="${cmdline##*${sep}}"
rlm@1 77
rlm@1 78 if [ -z "$manline" ]; then
rlm@1 79 description='No manpage'
rlm@1 80 #the "l" at the beginning is just to make it sort after "lib"
rlm@1 81 echo -n "lmanlessbin $cmdname" | sanitize
rlm@1 82 echo
rlm@1 83
rlm@1 84 else # if [ ! -z "$manline" ]; then
rlm@1 85 manpage=${manline##*${sep}}
rlm@1 86 manpagedir=${manpage%/*}
rlm@1 87 wsc='[[:space:],]\+'
rlm@1 88 # The cd $manpagedir is a workaround for a bug in man-db that causes it
rlm@1 89 # to attempt to resolve paths relative to cwd.
rlm@1 90 # The `t l;d;:l;n;b l' in the sed command is voodoo magic to make sed
rlm@1 91 # output only the first match but to keep eating up all input. I use this
rlm@1 92 # instead of `| head -n 1', because head breaks the pipe after doing
rlm@1 93 # its 1 line output, which (if it happens before sed has processed the
rlm@1 94 # complete input) freaks out man and causes it to emit a totally
rlm@1 95 # silly error message including "No such file or directory", which is
rlm@1 96 # annoying when you do testing without suppressing man's errors.
rlm@1 97 # The $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' removes the backspace-based
rlm@1 98 # as well as ESC-based formatting from man's output.
rlm@1 99 description="$( { cd "$manpagedir/.." 2>/dev/null ;
rlm@1 100 COLUMNS=300 man "$manpage" 2>/dev/null ||
rlm@1 101 echo " $name - Broken manpage" ; } |
rlm@1 102 sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' |
rlm@1 103 sed -n "/^NAME/,/^[A-Z]/s/^.*${wsc}${cmdname}${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )"
rlm@1 104 if [ -z "$description" ]; then
rlm@1 105 description="$( cd "$manpagedir/.." 2>/dev/null ;
rlm@1 106 COLUMNS=300 man "$manpage" 2>/dev/null |
rlm@1 107 sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' |
rlm@1 108 sed -n "s/^.*${wsc}..*${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )"
rlm@1 109 fi
rlm@1 110 test -z "$description" && description="Weird manpage"
rlm@1 111 fi
rlm@1 112
rlm@1 113 echo -n "binexe $cmdname" | sanitize
rlm@1 114 test "$linktarget" != '(->)' && echo -n "$linktarget" | sanitize
rlm@1 115 echo
rlm@1 116 #the "lx" in "lxdescription" is just to make sure it sorts after "lmanlessbin"
rlm@1 117 echo -n "lxdescription $cmdname: $description" | sanitize
rlm@1 118 echo
rlm@1 119 }
rlm@1 120
rlm@1 121 # NOTE: The -path and -lname stuff at the beginning of the following is
rlm@1 122 # there to make sure that none of the lines output by find contains
rlm@1 123 # a) \n or \r, because that would mess up post-processing the output
rlm@1 124 # line-by-line.
rlm@1 125 # b) \x7f, because this character triggers one of the nastier bash-bugs
rlm@1 126 # wrt string handling
rlm@1 127 # c) \2, because I use this as separator within the lines
rlm@1 128 # (Why \2 and not \0 or \1 ? Because bash can't cope with \0 at all and has
rlm@1 129 # bugs related to \1.)
rlm@1 130 #
rlm@1 131 # Because of this, having the final section called "ALL FILES" is technically
rlm@1 132 # a lie, because files with a path containing one of the abovementioned
rlm@1 133 # characters will not appear in output.
rlm@1 134 # However, a) no sane package contains such files
rlm@1 135 # b) they will be listed in the output from list_suspicious_files
rlm@1 136 cmd=(\( -path $'*\n*' -or -path $'*\r*' -or -path $'*\x7f*'
rlm@1 137 -or -path $'*\2*'
rlm@1 138 -or -lname $'*\n*' -or -lname $'*\r*' -or -lname $'*\x7f*'
rlm@1 139 -or -lname $'*\2*'
rlm@1 140 \)
rlm@1 141 -or
rlm@1 142 \(
rlm@1 143 \( -printf "zall %p\n" \) ,
rlm@1 144 \(
rlm@1 145 \( -type f -or -xtype f \) -and
rlm@1 146 \(
rlm@1 147 \( -perm -u+x \( -path "*/bin/*" -or -path "*/sbin/*" \) -printf 'command\2%f\2cmd\2(->%l)\n' \)
rlm@1 148 -or \( -path "*/man/man*/*" -exec "$0" ":man" {} \; \)
rlm@1 149 -or \( -path "*/man/*/man*/*" -exec "$0" ":mani" {} \; \)
rlm@1 150 -or \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" -exec "$0" ":lib" {} \; \)
rlm@1 151 -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 152 \)
rlm@1 153 \)
rlm@1 154 \)
rlm@1 155 )
rlm@1 156
rlm@1 157 forall_direntries_from "$ugname" "${cmd[@]}" | sort -u |
rlm@1 158 {
rlm@1 159 sep=$'\2'
rlm@1 160 hold=''
rlm@1 161 for((;;))
rlm@1 162 do
rlm@1 163 if [ -z "$hold" ]; then
rlm@1 164 read -r line || break
rlm@1 165 else
rlm@1 166 line="$hold"
rlm@1 167 hold=''
rlm@1 168 fi
rlm@1 169
rlm@1 170 case "z$line" in
rlm@1 171 zcommand${sep}*${sep}cmd${sep}*)
rlm@1 172 cmdname=${line#command${sep}}
rlm@1 173 cmdname=${cmdname%%${sep}*}
rlm@1 174 read -r hold
rlm@1 175 case "z$hold" in
rlm@1 176 zcommand${sep}${cmdname}${sep}man${sep}*|zcommand${sep}${cmdname}${sep}mani${sep}*)
rlm@1 177 expand_command "$cmdname" "$line" "$hold"
rlm@1 178 hold=''
rlm@1 179 ;;
rlm@1 180
rlm@1 181 z*)
rlm@1 182 expand_command "$cmdname" "$line" ""
rlm@1 183 ;;
rlm@1 184 esac
rlm@1 185 ;;
rlm@1 186
rlm@1 187 zcommand${sep}*${sep}man${sep}*|command${sep}*${sep}mani${sep}*)
rlm@1 188
rlm@1 189 echo -n "manextra ${line##*${sep}}" | sanitize
rlm@1 190 echo
rlm@1 191 ;;
rlm@1 192
rlm@1 193 z*)
rlm@1 194 echo -n "$line" | sanitize
rlm@1 195 echo
rlm@1 196 ;;
rlm@1 197 esac
rlm@1 198
rlm@1 199 done
rlm@1 200 } | sort | #no -u here, bc. the above processing may equalize different files
rlm@1 201 {
rlm@1 202 # (1) binexe: Executables (in *bin/)
rlm@1 203 # (2) lib: Libraries (in *lib/*)
rlm@1 204 # (3) lmanlessbin: Executables (in *bin/) without manpages
rlm@1 205 # (4) lxdescription: Summaries for executables (in *bin/)
rlm@1 206 # (5) manextra: Extra manpages
rlm@1 207 # full paths, no perms
rlm@1 208 # (6) nobinexe: Executables not in *bin/ (excluding *lib/*.so and *lib/*.so.*)
rlm@1 209 # full paths, no perms
rlm@1 210 # (7) zall: All files
rlm@1 211 # full paths, no perms
rlm@1 212
rlm@1 213 curstate=''
rlm@1 214 while read -r line
rlm@1 215 do
rlm@1 216 newstate="${line%% *}"
rlm@1 217 if [ "$newstate" != "$curstate" ]; then
rlm@1 218 curstate="$newstate"
rlm@1 219 case "$curstate" in
rlm@1 220 binexe)
rlm@1 221 echo 'EXECUTABLES (in */bin or */sbin)'
rlm@1 222 echo -n " ${line#binexe }"
rlm@1 223 ;;
rlm@1 224 lib)
rlm@1 225 echo
rlm@1 226 echo
rlm@1 227 echo 'LIBRARIES (lib*.a or lib*.so)'
rlm@1 228 echo -n " ${line#lib }"
rlm@1 229 ;;
rlm@1 230 lmanlessbin)
rlm@1 231 echo
rlm@1 232 echo
rlm@1 233 echo 'EXECUTABLES WITH NO MANPAGE (in */bin or */sbin)'
rlm@1 234 echo -n " ${line#lmanlessbin }"
rlm@1 235 ;;
rlm@1 236 lxdescription)
rlm@1 237 echo
rlm@1 238 echo
rlm@1 239 echo 'MANPAGE SUMMARIES OF EXECUTABLES (in */bin or */sbin)'
rlm@1 240 echo " ${line#lxdescription }"
rlm@1 241 ;;
rlm@1 242 manextra)
rlm@1 243 echo
rlm@1 244 echo 'EXTRA MANPAGES'
rlm@1 245 echo " ${line#manextra }"
rlm@1 246 ;;
rlm@1 247 nobinexe)
rlm@1 248 echo
rlm@1 249 echo 'EXTRA EXECUTABLES (not in */bin or */sbin)'
rlm@1 250 echo " ${line#nobinexe }"
rlm@1 251 ;;
rlm@1 252 zall)
rlm@1 253 echo
rlm@1 254 echo 'ALL FILES'
rlm@1 255 echo " ${line#zall }"
rlm@1 256 ;;
rlm@1 257 *)
rlm@1 258 echo
rlm@1 259 echo
rlm@1 260 echo 'UNEXPECTED LINE'
rlm@1 261 echo " $line"
rlm@1 262 ;;
rlm@1 263
rlm@1 264 esac
rlm@1 265 else
rlm@1 266 case "$curstate" in
rlm@1 267 binexe) echo -n ", ${line#binexe }"
rlm@1 268 ;;
rlm@1 269 lib) echo -n ", ${line#lib }"
rlm@1 270 ;;
rlm@1 271 lmanlessbin) echo -n ", ${line#lmanlessbin }"
rlm@1 272 ;;
rlm@1 273 lxdescription) echo " ${line#lxdescription }"
rlm@1 274 ;;
rlm@1 275 manextra) echo " ${line#manextra }"
rlm@1 276 ;;
rlm@1 277 nobinexe) echo " ${line#nobinexe }"
rlm@1 278 ;;
rlm@1 279 zall) echo " ${line#zall }"
rlm@1 280 ;;
rlm@1 281 *)
rlm@1 282 echo
rlm@1 283 echo 'UNEXPECTED LINE'
rlm@1 284 echo " $line"
rlm@1 285 ;;
rlm@1 286 esac
rlm@1 287 fi
rlm@1 288 done
rlm@1 289 }
rlm@1 290
rlm@1 291 list_suspicious_files_from "$ugname"