[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: X authentication and su (Re: changing framebuffer device owner during login)



   Here's another improved version of sux.

   I believe that all the previous issues have been solved. Most notably
the cookies should not appear on any command line anywhere at
anytime, anymore.

   So I believe that this version should be really secure now. Or does
someone see a way for a third party to snarf the cookies while sux is
running?

   Features:
 * 'sux user' and 'sux - user' behave just like su but transfer
   $DISPLAY and the X cookies.
 * You can specify a command on the command line, even if it contains
   spaces. This is likely to be buggy (double-quotes...) but should be
   enough to let you type: sux - xterm -title "Root's XTerm"
 * you can generate untrusted / temporary cookies for that user using
   '--untrusted' and/or '--timeout xxx' (see xauth generate)
 * if you symlink suxterm to sux, then 'suxterm - foo' will create a
   new xterm for user foo. This makes '--timeout' more usable: the
   cookie will remain valid as long as this xterm is open. So you could
   type 'suxterm --timeout 20 - foo' and the cookie will expire 20
   seconds after the xterm has been closed. (It also means you have 20
   seconds to type your password. Also it's equivalent to 'sux --timeout
   20 - foo xterm', but the script could be modified to have a timeout
   by default... (Inspired by Daniel Martin)
 * 'sux -' will use the standard XAUTHORITY trick. It's the simplest
   way to do it for root, hence the better way. Of course the
   exception is if you use '--untrusted' or '--timeout', then sux will 
   do it as usual.
 * You can use the su '--preserve-environment' option. In that case sux
   will override XAUTHORITY to the so that xauth does not try to use the
   original user's .Xauthority file (which it obviously could not
   do anyway due to access rights).
 * sux should work even for those using csh. But I'm not using it myself
   so I could not check. Let me know if there's a problem.


--- cut here ---
#!/bin/sh

usage()
{
  echo "usage: `basename $0` [-m] [-p] [--preserve-environment]" >&2
  echo "           [--untrusted] [--timeout x]" >&2
  echo "           [-] [username [command]]" >&2
  exit 2
}


##
# Process the sux options
sux_su_opts=""
sux_preserve=""
sux_got_minus=0
sux_username=""
sux_untrusted=""
sux_timeout=""
while [ $# -gt 0 ]
do
  if [ "$sux_got_minus" = "1" ]
  then
    # Username follows "-"
    sux_username="$1"
    sux_su_opts="$sux_su_opts $1"
    shift
    break
  elif [ "$1" = "-" ]
  then
    # Last option before the username
    sux_su_opts="$sux_su_opts $1"
    sux_got_minus=1
    shift
  elif [ "$1" = "-m" -o "$1" = "-p" -o "$1" = "--preserve-environment" ]
  then
    sux_preserve="1"
    sux_su_opts="$sux_su_opts $1"
    shift
  elif [ "$1" = "--untrusted" ]
  then
    sux_untrusted="untrusted"
    shift
  elif [ "$1" = "--timeout" ]
  then
    if [ $# -lt 2 ]
    then
      # Complain and exit
      usage
    fi
    sux_timeout="timeout $2"
    shift 2
  elif [ "$1" = "-?" ]
  then
    # Complain and exit
    usage
  else
    # First non-option is the username
    sux_username="$1"
    sux_su_opts="$sux_su_opts $1"
    shift
    break
  fi
done

if [ -z "$DISPLAY" ]
then
  # If DISPLAY is not set we can take a shortcut...
  if [ -n "$sux_untrusted" -o -n "$sux_timeout" ]
  then
    echo "--untrusted and --timeout are only supported if DISPLAY is set" >&2
    usage
  fi
  exec su $sux_su_opts "$@"
fi

##
# Create new cookies / retrieve the existing ones if necessary
if [ -n "$sux_untrusted" -o -n "$sux_timeout" ]
then
  # Yeah, tempfile is a debian-specific command.  Workarounds exist for
  # other machines.  If you do use a workaround, be certain that said
  # workaround actually creates the new file, (via touch or similar) or
  # xauth (below) will produce a pointless warning message.

  sux_tmpfile=`tempfile -p sux`
  xauth -q -f $sux_tmpfile generate $DISPLAY . $sux_untrusted $sux_timeout
  sux_xauth_data=`xauth -f $sux_tmpfile list $DISPLAY`
  rm -f $sux_tmpfile
fi

## 
# Build the command to restore the cookies in the su
if [ -n "$sux_xauth_data" -o \( -n "$sux_username" -a "$sux_username" != "root" \) ]
then
  # If display is of the form "host:x.y" we may also need to get the 
  # cookies for "host/unix:x.y".
  sux_unix_display=`echo $DISPLAY | sed -e 's#^\([a-zA-Z_.][a-zA-Z_.]*\):#\1/unix:#'`
  if [ "$DISPLAY" = "$sux_unix_display" ]
  then
    sux_unix_display=""
  fi

  # Get the cookies if we don't have them already
  if [ -z "$sux_xauth_data" ]
  then
    # Get the cookies. Note that we may need to 
    sux_xauth_data=`xauth -q list $DISPLAY`
    if [ -n "$sux_unix_display" ]
    then
      sux_xauth_data="$sux_xauth_data `xauth -q list $sux_unix_display`"
    fi
  fi

  # We highjack the TERM environment variable to transfer the cookies to the 
  # other user. We do this so that they never appear on any command line, and 
  # because TERM appears to be the only environment variable that is not 
  # reset by su.
  # Now, as long as 'echo' is a shell builtin, these cookies will never 
  # appear as command line arguments.
  sux_term="TERM='$TERM'"
  # now we can store the script that will restore the cookies on the other 
  # side of the su, in TERM!

  # Remove the old cookies. They may cause trouble if we transfer only one 
  # cookie, e.g. an MIT cookie, and there's still a stale XDM cookie hanging 
  # around.
  export TERM="xauth -q remove $DISPLAY 2>/dev/null;"
  if [ -n "$sux_unix_display" ]
  then
    TERM="$TERM xauth -q remove $sux_unix_display;"
  fi

  # Note that there may be more than one cookie to transfer, hence 
  # this loop
  sux_i=0
  for sux_str in $sux_xauth_data
  do
    if [ $sux_i -eq 0 ]
    then
      TERM="$TERM echo add $sux_str"
    else
      TERM="$TERM $sux_str"
    fi
    sux_i=`expr $sux_i + 1`
    if [ $sux_i -eq 3 ]
    then
      TERM="$TERM | xauth source - ;"
      sux_i=0
    fi
  done
  sux_xauth_cmd="eval \$TERM;"
  sux_xauthority=""
else
  # For root we can simplify things and simply access the original user's 
  # .Xauthority file.
  sux_term=""
  sux_xauth_cmd=""
  if [ -n "$XAUTHORITY" ]
  then
    sux_xauthority="XAUTHORITY='$XAUTHORITY'"
  else
    sux_xauthority="XAUTHORITY='$HOME/.Xauthority'"
  fi
fi

##
# And execute the specified command. We marshall the parameters a little 
# bit in an effort to support spaces in parameters. This should be enough 
# to get commands like 'xterm -title "My XTerm"' to work.
sux_cmd=""
if [ $# -gt 0 ]
then
  while [ $# -gt 0 ]
  do
    sux_cmd="$sux_cmd \"$1\""
    shift
  done
elif [ "`basename $0`" = "suxterm" ]
then
  # Start an xterm, useful for temporary cookies
  sux_cmd="xterm"
else
  # If no command is specified, start a shell
  if [ $# -eq 0 ]
  then
    if [ "$sux_got_minus" = "1" ]
    then
      sux_cmd="sh -c \"exec -l \$SHELL\""
    else
      sux_cmd="\$SHELL"
    fi
  fi
fi

##
# We would not want the other user to try and use our XAUTHORITY file. He 
# wouldn't have the proper access rights anyway...
unset XAUTHORITY

##
# Adapt the environment for the new user
if [ -n "$sux_preserve" -a -n "sux_xauth_cmd" ]
then
  XAUTHORITY=`egrep "^$sux_username:" /etc/passwd | cut -d: -f6`
  if [ -z "$XAUTHORITY" ]
  then
    echo "WARNING: --preserve-environment has been set, but no good value was found for XAUTHORITY, expect trouble" >&2
    unset XAUTHORITY
  else
    export XAUTHORITY="$XAUTHORITY/.Xauthority"
  fi
fi

##
# Execute su
exec su $sux_su_opts -c "$sux_xauth_cmd \
     exec env $sux_xauthority $sux_term DISPLAY='$DISPLAY' $sux_cmd;"
--- cut here ---

--
Francois Gouget         fgouget@free.fr        http://fgouget.free.fr/
                           La terre est une bêta...



Reply to: