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

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



   Thanks to Falk Hueffner for pointing out that using the XAUTHORITY
trick for root may not work if the home directories are on NFS. Here is
his explanation:

> Because having root (UID 0) doesn't gain you access to all files on
> a NFS mount; otherwise, gaining root on any of the potentially very
> many machines that mount an NFS export could lead to much damage.
> This is known as 'root squash' and defaults to on.

   I also found another reason for not using XAUTHORITY when su-ing to
root: if root then goes on to do an xauth add/remove he will change the
ownership of the .Xauthority file, which will put the original user in
great trouble.

  So I did the following:
 * added three options to let you choose how to transfer the X
credentials:
    --no-cookies
      just transfer DISPLAY and assume that's enoguh
    --copy-cookies
      copy the cookies using xauth
    --use-xauthority
      set XAUTHORITY, only usable by root
 * I set the default to '--copy-cookies' for all cases, but you can
easily change the default for root by changing the
sux_root_cookie_transfer variable at the top of the file

   While I was at it I also added a '--display' command... I attached
the new version below (it's the last time I do this, I promise).


   Ok, I believe this should be quite stable now. I would like to submit
this for inclusion in Debian, especially since now Debian defaults to
basically disabling xhost (well, IIRC you get asked but there's a
definite bias in the question...)

   What would be the way to do this?

   My current plan is to submit a wishlist report against xbase-clients
at some point during the week-end. If I get confirmation that this can
go into Debian then I'll write a man page (otherwise it's just going to
be me using the script so it's not worth writing a man page).


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

# Written by Francois Gouget, fgouget@free.fr


# How to transfer cookies for root. See the sux_cookie_transfer variable
# for options. Note that use-xauthority may not work if home directories
# are on NFS. In such a case, change the default to copy-cookies.
sux_root_cookie_transfer="c"


usage()
{
  echo "usage: `basename $0` [-m|-p|--preserve-environment]" >&2
  echo "           [--display display]" >&2
  echo "           [--no-cookies|--copy-cookies|--use-xauthority]" >&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
# "" -> default, n -> no-cookies, c -> copy-cookies, x -> use-xauthority
sux_cookie_transfer=""
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
    # The remainder is the command to be executed
    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" = "--display" ]
  then
    if [ $# -lt 2 ]
    then
      echo "--display takes a display name as an argument" >&2
      usage # exits
    fi
    export DISPLAY="$2"
    shift 2
  elif [ "$1" = "--no-cookies" ]
  then
    if [ -n "$sux_cookie_transfer" -a "$sux_cookie_transfer" != "n" ]
    then
      echo "--no-cookies is incompatible with --copy-cookies and --use-xauthority" >&2
      usage # exits
    fi
    sux_cookie_transfer="n"
    shift
  elif [ "$1" = "--copy-cookies" ]
  then
    if [ -n "$sux_cookie_transfer" -a "$sux_cookie_transfer" != "c" ]
    then
      echo "--copy-cookies is incompatible with --no-cookies and --use-xauthority" >&2
      usage # exits
    fi
    sux_cookie_transfer="c"
    shift
  elif [ "$1" = "--use-xauthority" ]
  then
    if [ -n "$sux_cookie_transfer" -a "$sux_cookie_transfer" != "x" ]
    then
      echo "--use-xauthority is incompatible with --no-cookies and --copy-cookies" >&2
      usage # exits
    fi
    sux_cookie_transfer="x"
    shift
  elif [ "$1" = "--untrusted" ]
  then
    sux_untrusted="untrusted"
    shift
  elif [ "$1" = "--timeout" ]
  then
    if [ $# -lt 2 ]
    then
      echo "--timeout takes a timeout in seconds" >&2
      usage # exits
    fi
    sux_timeout="timeout $2"
    shift 2
  elif [ "$1" = "-?" ]
  then
    usage # exits
  else
    # First non-option is the username
    sux_username="$1"
    sux_su_opts="$sux_su_opts $1"
    shift
    # The remainder is the command to be executed
    break
  fi
done


##
# Get rid of the simple case
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 #exits
  fi
  exec su $sux_su_opts "$@"
fi


##
# Do some option checking
if [ -z "$sux_username" ]
then
  sux_username="root"
fi
if [ -z "$sux_cookie_transfer" ]
then
  if [ "$sux_username" = "root" ]
  then
    sux_cookie_transfer="$sux_root_cookie_transfer"
  else
    sux_cookie_transfer="c"
  fi
fi
if [ "$sux_cookie_transfer" = "x" -a "$sux_username" != "root" ]
then
  echo "Only root can use --use-xauthority" >&2
  usage # exits
fi


##
# Create new cookies / retrieve the existing ones if necessary
if [ -n "$sux_untrusted" -o -n "$sux_timeout" ]
then
  if [ "$sux_cookie_transfer" != "c" ]
  then
    echo "--no-cookies/--use-xauthority are incompatible with --untrusted/--timeout" >&2
    usage #exits
  fi

  # 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 [ "$sux_cookie_transfer" = "c" ]
then
  # --copy-cookies case. We copy the cookie(s) using the xauth command.

  # 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. Then, as long as 'echo' is a shell builtin, these cookies 
  # will never appear as command line arguments which means noone will be 
  # able to intercept them (assuming they were safe in the first place).
  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=""
elif [ "$sux_cookie_transfer" = "x" ]
then
  # --use-xauthority case. 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
else
  # --no-cookies case. We just transfer $DISPLAY and assume the 
  # target user already has the necessary cookies
  sux_term=""
  sux_xauth_cmd=""
  sux_xauthority=""
fi


##
# Marshall the specified command in an effort to support parameters that 
# contain spaces. 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


##
# --preserve-environment special case
if [ -n "$sux_preserve" -a -n "$sux_xauth_cmd" ]
then
  sux_home=`egrep "^$sux_username:" /etc/passwd | cut -d: -f6`
  if [ -z "$sux_home" ]
  then
    echo "WARNING: --preserve-environment has been set, but no good value was found for XAUTHORITY, expect trouble" >&2
  else
    export XAUTHORITY="$sux_home/.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: