COMMAND

    emacs

SYSTEMS AFFECTED

    Emacs up to 20.6

PROBLEM

    Following is based on RUS-CERT Advisory 200004-01: GNU Emacs 20.
    Several vulnerabilities were discovered in all Emacs versions up
    to 20.6, namely:

        - Under  certain circumstances,  unprivileged local  users can
          eavesdrop   the   communication   between   Emacs   and  its
          subprocesses
        - It  is  impossible  to  safely  create temporary files in  a
          public directory from Emacs Lisp
        - The history of recently typed keys may expose passwords

    Especially the first two  vulnerabilities seriously impact the use
    of tools like Mailcrypt in a multi-user environment.

    Improper permissions on slave PTYs
    ==================================
    Affected systems so far were found:

        - Linux (both libc and glibc)
        - FreeBSD (and probably other *BSD variants)
        - HP-UX 10.x, 11.00
        - AIX 4

    On the other side, unaffected systems are:

        - Solaris  (The Solaris  runtime system  automatically adjusts
          the PTY permissions.)
        - Data General's  DG/UX seems to  be unaffected, according  to
          the source code.  Other systems have not been examined.

    Severity is high  in multi-user environments,  low otherwise.   On
    the systems listed above, when  a new subprocess is created  using
    the builtin Lisp function start-process, Emacs doesn't set  proper
    permissions for the  slave PTY device.   Unprivileged local  users
    can eavesdrop  the data  which Emacs  sends to  its subprocess and
    fake responses  from the  subprocess. This  impacts Emacs packages
    such  as  Mailcrypt,  which  transmit  (among  other  things)  PGP
    passphrases over this data channel.


    Unsafe creation of temporary files
    ==================================

    Affected  are  All  Unix-like  Emacs  platforms  on  which  public
    directories are used to store  temporary files.  Severity is  high
    in multi-user environments,  low otherwise.   Emacs Lisp does  not
    provide any functionality to create a file in a publicly  writable
    directory  in   a  safe   way.   Many   Emacs  packages   use  the
    make-temp-name Lisp function to create names for temporary  files.
    These names are not very hard to guess.  Because it is  impossible
    to create the  actual temporary file  in a safe  manner, the usual
    symlink attacks are likely successful.


    Passwords are stored in the key history
    =======================================

    Affected are all  platforms and severity  is low.   Functions like
    read-passwd do not clear the  the history of recently typed  keys.
    In fact, there is  no way to do  that from Emacs Lisp.   Passwords
    might be recovered by someone who has got access to the console on
    which  Emacs  is  running,  subverting  password  expiring as, for
    example, provided by  Mailcrypt.  (Usually,  there are many  other
    ways to obtain passwords  if you can type  C-h l inside a  foreign
    Emacs, though)

    Acknowledgements goes  to Helmut  Waitzmann for  rediscovering the
    PTY  permissions  problem  and  testing  the  HP-UX  patch.   Gerd
    Moellmann  of  the  Emacs  development  team  for  the  patch   to
    clear-this-command-keys and helpful comments.

SOLUTION

    Improper permissions on slave PTYs
    ==================================
    At Emacs Lisp  level, the only  workaround is to  use call-process
    instead of start-process. Of course, this is not always an  option
    because the functionality provided  by these functions is  not the
    same  (synchronous  vs.  asynchronous  subprocesses).   The   real
    solution requires  modification of  the Emacs  C source  code.   A
    patch for  Emacs 20.6  is included  below which  enables Emacs  to
    Unix98 PTYs.  The patch is known to work on the following systems:
        - Linux with glibc 2.1
        - AIX 4.2
        - HP-UX 11.00

    It is expected to work on HP-UX 10.x as well. (Under some versions
    of  HP-UX,  grantpt()  does  not  behave  as specified.  The patch
    contains a suitable workaround)

    Unfortunately, systems lacking Unix98 support (such as Linux  with
    libc5  and  glibc  2.0,  FreeBSD  and  AIX 3) require a completely
    different  fix  and  a  setuid  root  binary  to  change  the  PTY
    permissions (in other  words: some kind  of userspace Unix  98 PTY
    emulation). There are no plans to provide this emulation; Unix  98
    PTYs are already widely adopted and most Unix derivatives  provide
    them (with the notable exception  of several *BSD variants).   For
    FreeBSD, an enhancement to openpty() has been proposed which  sets
    proper permissions  on the  slave TTY  device (see  problem report
    bin/9770).  The  proposal has yet  to be adopted,  though.  Future
    Emacs releases will contain a similar fix.

    Unsafe creation of temporary files
    ==================================
    Emacs  21  will  provide  a  new  make-temp-file  function  (which
    creates the file  in question in  safe way) and  the functionality
    to safely create temporary files.  In the meantime, until Emacs 21
    is  released  and  package  maintainers  adopt  the  new function,
    private  directories  for  temporary  files  should be used.  Most
    packages provide variables for  that. For example, for  Mailcrypt,
    the  variable  mc-temp-directory  has  to  be  set, and for Python
    Mode, it's py-temp-directory.

    Passwords are stored in the key history
    =======================================
    The patch  below adds  code to  clear-this-command-keys which will
    erase the  vector containing  the last  100 events.   In the past,
    this function was already used as if it behaved that way.

    The patch  below is  against GNU  Emacs 20.6,  as available at GNU
    FTP mirrors.  Note that you  have to run autoconf to recreate  the
    configure script (including it would have enormously increased the
    size of the patch):

    diff --unified --recursive emacs-20.6-orig/configure.in emacs-20.6/configure.in
    --- emacs-20.6-orig/configure.in	Sat Feb 26 13:07:02 2000
    +++ emacs-20.6/configure.in	Fri Mar 10 19:13:05 2000
    @@ -1636,6 +1636,11 @@
     strerror fpathconf select mktime euidaccess getpagesize tzset setlocale \
     utimes setrlimit setpgid getcwd shutdown strftime)
    
    +# Check for UNIX98 PTYs.
    +# getpt is a glibc addition which emulates the master device on
    +# systems without kernel support.
    +AC_CHECK_FUNCS(grantpt unlockpt getpt ptsname)
    +
     # Check this now, so that we will NOT find the above functions in ncurses.
     # That is because we have not set up to link ncurses in lib-src.
     # It's better to believe a function is not available
    diff --unified --recursive emacs-20.6-orig/src/config.in emacs-20.6/src/config.in
    --- emacs-20.6-orig/src/config.in	Mon Apr 26 07:19:44 1999
    +++ emacs-20.6/src/config.in	Fri Mar 10 19:13:05 2000
    @@ -235,6 +235,14 @@
     #undef HAVE_SHUTDOWN
     #undef HAVE_STRFTIME
    
    +/* UNIX98 PTY support functions
    +   getpt is a glibc addition which emulates the master device on
    +   systems without kernel support. */
    +#undef HAVE_GRANTPT
    +#undef HAVE_UNLOCKPT
    +#undef HAVE_GETPT
    +#undef HAVE_PTSNAME
    +
     #undef LOCALTIME_CACHE
     #undef HAVE_INET_SOCKETS
    
    diff --unified --recursive emacs-20.6-orig/src/keyboard.c emacs-20.6/src/keyboard.c
    --- emacs-20.6-orig/src/keyboard.c	Thu Nov 18 05:57:32 1999
    +++ emacs-20.6/src/keyboard.c	Fri Mar 10 19:13:05 2000
    @@ -8318,10 +8318,18 @@
    
     DEFUN ("clear-this-command-keys", Fclear_this_command_keys,
       Sclear_this_command_keys, 0, 0, 0,
    -  "Clear out the vector that `this-command-keys' returns.")
    +  "Clear out the vector that `this-command-keys' returns.\n\
    +Clear vector containing last 100 events.")
       ()
     {
    +  int i;
    +
       this_command_key_count = 0;
    +
    +  for (i = 0; i < XVECTOR (recent_keys)->size; ++i)
    +    XVECTOR (recent_keys)->contents[i] = Qnil;
    +  total_keys = 0;
    +  recent_keys_index = 0;
       return Qnil;
     }
    
    diff --unified --recursive emacs-20.6-orig/src/s/aix4.h emacs-20.6/src/s/aix4.h
    --- emacs-20.6-orig/src/s/aix4.h	Sat Jul 25 08:45:27 1998
    +++ emacs-20.6/src/s/aix4.h	Fri Mar 17 20:44:08 2000
    @@ -12,3 +12,33 @@
     /* Specify the type that the 3rd arg of `accept' points to.
        It is just a guess which versions of AIX need this definition.  */
     #define SOCKLEN_TYPE int
    +
    +#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
    +/* UNIX98 PTYs are available.
    +   Added by Florian Weimer <Florian.Weimer@RUS.Uni-Stuttgart.DE>,
    +   RUS-CERT, University of Stuttgart.  Based on Emacs code for DGUX. */
    +
    +/* Most of the #defines are already provided by aix3-1.h. */
    +
    +/* This sets the name of the slave side of the PTY.  grantpt(3) and
    +   unlockpt(3) may fork a subprocess, so keep sigchld_handler() from
    +   intercepting that death. */
    +
    +#undef  PTY_TTY_NAME_SPRINTF
    +#define PTY_TTY_NAME_SPRINTF			\
    +  {						\
    +    char *ptsname(), *ptyname;			\
    +						\
    +    sigblock(sigmask(SIGCHLD));			\
    +    if (grantpt(fd) == -1)			\
    +      fatal("could not grant slave pty");	\
    +    if (unlockpt(fd) == -1)			\
    +      fatal("could not unlock slave pty");	\
    +    sigunblock(sigmask(SIGCHLD));		\
    +    if (!(ptyname = ptsname(fd)))		\
    +      fatal ("could not enable slave pty");	\
    +    strncpy(pty_name, ptyname, sizeof(pty_name)); \
    +    pty_name[sizeof(pty_name) - 1] = 0;		\
    +  }
    +
    +#endif
    diff --unified --recursive emacs-20.6-orig/src/s/gnu-linux.h emacs-20.6/src/s/gnu-linux.h
    --- emacs-20.6-orig/src/s/gnu-linux.h	Wed Jan 26 14:28:40 2000
    +++ emacs-20.6/src/s/gnu-linux.h	Fri Mar 17 20:44:31 2000
    @@ -307,3 +307,49 @@
     #ifdef DOUG_LEA_MALLOC
     #undef REL_ALLOC
     #endif
    +
    +#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
    +/* UNIX98 PTYs are available.
    +   Added by Florian Weimer <Florian.Weimer@RUS.Uni-Stuttgart.DE>,
    +   RUS-CERT, University of Stuttgart.  Based on Emacs code for DGUX. */
    +
    +#define PTY_ITERATION for (i = 0; i < 1; i++)
    +/* no iteration at all */
    +
    +/* Use getpt() if it's available, because it provides Unix98 PTY
    +   emulation for kernels which doesn't support it natively. */
    +
    +#ifdef HAVE_GETPT
    +#define PTY_OPEN                                 \
    +  do {                                           \
    +    fd = getpt();                             \
    +    if (fcntl (fd, F_SETFL, O_NDELAY) == -1)  \
    +      fatal ("could not set master PTY to non-block mode"); \
    +  } while (0)
    +
    +#else
    +/* the master PTY device */
    +#define PTY_NAME_SPRINTF strcpy (pty_name, "/dev/ptmx");
    +#endif
    +
    +/* This sets the name of the slave side of the PTY.  grantpt(3) and
    +   unlockpt(3) may fork a subprocess, so keep sigchld_handler() from
    +   intercepting that death. */
    +
    +#define PTY_TTY_NAME_SPRINTF			\
    +  {						\
    +    char *ptsname(), *ptyname;			\
    +						\
    +    sigblock(sigmask(SIGCHLD));			\
    +    if (grantpt(fd) == -1)			\
    +      fatal("could not grant slave pty");	\
    +    if (unlockpt(fd) == -1)			\
    +      fatal("could not unlock slave pty");	\
    +    if (!(ptyname = ptsname(fd)))		\
    +      fatal ("could not enable slave pty");	\
    +    strncpy(pty_name, ptyname, sizeof(pty_name)); \
    +    pty_name[sizeof(pty_name) - 1] = 0;		\
    +    sigunblock(sigmask(SIGCHLD));		\
    +  }
    +
    +#endif
    diff --unified --recursive emacs-20.6-orig/src/s/hpux.h emacs-20.6/src/s/hpux.h
    --- emacs-20.6-orig/src/s/hpux.h	Mon Jan 15 10:16:40 1996
    +++ emacs-20.6/src/s/hpux.h	Wed Mar 29 08:40:52 2000
    @@ -228,6 +228,59 @@
     /* This is needed for HPUX version 6.2; it may not be needed for 6.2.1.  */
     #define SHORT_CAST_BUG
    
    +#if defined(HAVE_GRANTPT) && defined(HAVE_UNLOCKPT) && defined(HAVE_PTSNAME)
    +/* UNIX98 PTYs are available.
    +   Added by Florian Weimer <Florian.Weimer@RUS.Uni-Stuttgart.DE>,
    +   RUS-CERT, University of Stuttgart.  Based on Emacs code for DGUX. */
    +
    +#ifdef emacs
    +#include <grp.h>
    +#include <sys/stropts.h>
    +#endif
    +
    +#define PTY_ITERATION for (i = 0; i < 1; i++)
    +/* no iteration at all */
    +
    +/* the master PTY device */
    +#define PTY_NAME_SPRINTF strcpy (pty_name, "/dev/ptmx");
    +
    +/* This sets the name of the slave side of the PTY.  grantpt(3) and
    +   unlockpt(3) may fork a subprocess, so keep sigchld_handler() from
    +   intercepting that death.  grantpt() behavior on HP-UX differs from
    +   what's specified in the man page: the group of the slave PTY is set
    +   to the user's primary group, and we fix that.  */
    +
    +#define PTY_TTY_NAME_SPRINTF			\
    +  {						\
    +    char *ptsname(), *ptyname;			\
    +    struct group *getgrnam (), *tty_group = getgrnam ("tty"); \
    +    if (tty_group == NULL)                      \
    +      fatal ("group tty not found");            \
    +						\
    +    sigblock(sigmask(SIGCHLD));			\
    +    if (grantpt(fd) == -1)			\
    +      fatal("could not grant slave pty");	\
    +    if (!(ptyname = ptsname(fd)))		\
    +      fatal ("could not enable slave pty");	\
    +    strncpy(pty_name, ptyname, sizeof(pty_name)); \
    +    pty_name[sizeof(pty_name) - 1] = 0;		\
    +    if (chown (pty_name, (uid_t) -1, tty_group->gr_gid) == -1) \
    +      fatal ("could not chown slave pty");      \
    +    if (unlockpt(fd) == -1)			\
    +      fatal("could not unlock slave pty");	\
    +    sigunblock(sigmask(SIGCHLD));		\
    +  }
    +
    +/* Push various streams modules onto a PTY channel. */
    +
    +#define SETUP_SLAVE_PTY \
    +  if (ioctl (xforkin, I_PUSH, "ptem") == -1)	\
    +    fatal ("ioctl I_PUSH ptem", errno);		\
    +  if (ioctl (xforkin, I_PUSH, "ldterm") == -1)	\
    +    fatal ("ioctl I_PUSH ldterm", errno);
    +
    +#else /* no UNIX98 PTYs */
    +
     /* This is how to get the device name of the tty end of a pty.  */
     #define PTY_TTY_NAME_SPRINTF \
                 sprintf (pty_name, "/dev/pty/tty%c%x", c, i);
    @@ -235,6 +288,8 @@
     /* This is how to get the device name of the control end of a pty.  */
     #define PTY_NAME_SPRINTF \
 	    sprintf (pty_name, "/dev/ptym/pty%c%x", c, i);
    +
    +#endif /* UNIX 98 PTYs */
    
     /* This triggers a conditional in xfaces.c.  */
     #define XOS_NEEDS_TIME_H