find-maint: Security

 
 12 Security
 ***********
 
 See ⇒Security Considerations (find)Security Considerations, for a
 full description of the findutils approach to security considerations
 and discussion of particular tools.
 
    If someone reports a security bug publicly, we should fix this as
 rapidly as possible.  If necessary, this can mean issuing a fixed
 release containing just the one bug fix.  We try to avoid issuing
 releases which include both significant security fixes and functional
 changes.
 
    Where someone reports a security problem privately, we generally try
 to construct and test a patch without pushing the intermediate code to
 the public repository.
 
    Once everything has been tested, this allows us to make a release and
 push the patch.  The advantage of doing things this way is that we avoid
 situations where people watching for git commits can figure out and
 exploit a security problem before a fixed release is available.
 
    It's important that security problems be fixed promptly, but don't
 rush so much that things go wrong.  Make sure the new release really
 fixes the problem.  It's usually best not to include functional changes
 in your security-fix release.
 
    If the security problem is serious, send an alert to
 <vendor-sec@lst.de>.  The members of the list include most GNU/Linux
 distributions.  The point of doing this is to allow them to prepare to
 release your security fix to their customers, once the fix becomes
 available.  Here is an example alert:-
 
      GNU findutils heap buffer overrun (potential privilege escalation)
 
 
 
      I. BACKGROUND
      =============
 
      GNU findutils is a set of programs which search for files on Unix-like
      systems.  It is maintained by the GNU Project of the Free Software
      Foundation.  For more information, see
      <https://www.gnu.org/software/findutils>.
 
 
      II. DESCRIPTION
      ===============
 
      When GNU locate reads filenames from an old-format locate database,
      they are read into a fixed-length buffer allocated on the heap.
      Filenames longer than the 1026-byte buffer can cause a buffer overrun.
      The overrunning data can be chosen by any person able to control the
      names of filenames created on the local system.  This will normally
      include all local users, but in many cases also remote users (for
      example in the case of FTP servers allowing uploads).
 
      III. ANALYSIS
      =============
 
      Findutils supports three different formats of locate database, its
      native format "LOCATE02", the slocate variant of LOCATE02, and a
      traditional ("old") format that locate uses on other Unix systems.
 
      When locate reads filenames from a LOCATE02 database (the default
      format), the buffer into which data is read is automatically extended
      to accommodate the length of the filenames.
 
      This automatic buffer extension does not happen for old-format
      databases.  Instead a 1026-byte buffer is used.  When a longer
      pathname appears in the locate database, the end of this buffer is
      overrun.  The buffer is allocated on the heap (not the stack).
 
      If the locate database is in the default LOCATE02 format, the locate
      program does perform automatic buffer extension, and the program is
      not vulnerable to this problem.  The software used to build the
      old-format locate database is not itself vulnerable to the same
      attack.
 
      Most installations of GNU findutils do not use the old database
      format, and so will not be vulnerable.
 
 
      IV. DETECTION
      =============
 
      Software
      --------
      All existing releases of findutils are affected.
 
 
      Installations
      -------------
 
      To discover the longest path name on a given system, you can use the
      following command (requires GNU findutils and GNU coreutils):
 
      find / -print0 | tr -c '\0' 'x' | tr '\0' '\n' | wc -L
 
      V. EXAMPLE
      ==========
 
      This section includes a shell script which determines which of a list
      of locate binaries is vulnerable to the problem.  The shell script has
      been tested only on glibc based systems having a mktemp binary.
 
      NOTE: This script deliberately overruns the buffer in order to
      determine if a binary is affected.  Therefore running it on your
      system may have undesirable effects.  We recommend that you read the
      script before running it.
 
      #! /bin/sh
      set +m
      if vanilla_db="$(mktemp nicedb.XXXXXX)" ; then
          if updatedb --prunepaths="" --old-format --localpaths="/tmp" \
      	--output="$@{vanilla_db@}" ; then
      	true
          else
      	rm -f "$@{vanilla_db@}"
      	vanilla_db=""
      	echo "Failed to create old-format locate database; skipping the sanity checks" >&2
          fi
      fi
      
      make_overrun_db() @{
          # Start with a valid database
          cat "$@{vanilla_db@}"
          # Make the final entry really long
          dd if=/dev/zero  bs=1 count=1500 2>/dev/null | tr '\000' 'x'
      @}
      
      
      
      ulimit -c 0
      
      usage() @{ echo "usage: $0 binary [binary...]" >&2; exit $1; @}
      [ $# -eq 0 ] && usage 1
      
      bad=""
      good=""
      ugly=""
      if dbfile="$(mktemp nasty.XXXXXX)"
      then
          make_overrun_db > "$dbfile"
          for locate ; do
            ver="$locate = $("$locate"  --version | head -1)"
            if [ -z "$vanilla_db" ] || "$locate" -d "$vanilla_db" "" >/dev/null ; then
      	  "$locate" -d "$dbfile" "" >/dev/null
      	  if [ $? -gt 128 ] ; then
      	      bad="$bad
      vulnerable: $ver"
      	  else
      	      good="$good
      good: $ver"
      	  fi
             else
      	  # the regular locate failed
      	  ugly="$ugly
      buggy, may or may not be vulnerable: $ver"
             fi
          done
          rm -f "$@{dbfile@}" "$@{vanilla_db@}"
          # good: unaffected.  bad: affected (vulnerable).
          # ugly: doesn't even work for a normal old-format database.
          echo "$good"
          echo "$bad"
          echo "$ugly"
      else
        exit 1
      fi
 
 
 
 
      VI. VENDOR RESPONSE
      ===================
 
      The GNU project discovered the problem while 'locate' was being worked
      on; this is the first public announcement of the problem.
 
      The GNU findutils mantainer has issued a patch as p[art of this
      announcement.  The patch appears below.
 
      A source release of findutils-4.2.31 will be issued on 2007-05-30.
      That release will of course include the patch.  The patch will be
      committed to the public CVS repository at the same time.  Public
      announcements of the release, including a description of the bug, will
      be made at the same time as the release.
 
      A release of findutils-4.3.x will follow and will also include the
      patch.
 
 
      VII. PATCH
      ==========
 
      This patch should apply to findutils-4.2.23 and later.
      Findutils-4.2.23 was released almost two years ago.
      Index: locate/locate.c
      ===================================================================
      RCS file: /cvsroot/findutils/findutils/locate/locate.c,v
      retrieving revision 1.58.2.2
      diff -u -p -r1.58.2.2 locate.c
      --- locate/locate.c	22 Apr 2007 16:57:42 -0000	1.58.2.2
      +++ locate/locate.c	28 May 2007 10:18:16 -0000
      @@@@ -124,9 +124,9 @@@@ extern int errno;
      
       #include "locatedb.h"
       #include <getline.h>
      -#include "../gnulib/lib/xalloc.h"
      -#include "../gnulib/lib/error.h"
      -#include "../gnulib/lib/human.h"
      +#include "xalloc.h"
      +#include "error.h"
      +#include "human.h"
       #include "dirname.h"
       #include "closeout.h"
       #include "nextelem.h"
      @@@@ -468,10 +468,36 @@@@ visit_justprint_unquoted(struct process_
         return VISIT_CONTINUE;
       @}
      
      +static void
      +toolong (struct process_data *procdata)
      +@{
      +  error (EXIT_FAILURE, 0,
      +	 _("locate database %s contains a "
      +	   "filename longer than locate can handle"),
      +	 procdata->dbfile);
      +@}
      +
      +static void
      +extend (struct process_data *procdata, size_t siz1, size_t siz2)
      +@{
      +  /* Figure out if the addition operation is safe before performing it. */
      +  if (SIZE_MAX - siz1 < siz2)
      +    @{
      +      toolong (procdata);
      +    @}
      +  else if (procdata->pathsize < (siz1+siz2))
      +    @{
      +      procdata->pathsize = siz1+siz2;
      +      procdata->original_filename = x2nrealloc (procdata->original_filename,
      +						&procdata->pathsize,
      +						1);
      +    @}
      +@}
      +
       static int
       visit_old_format(struct process_data *procdata, void *context)
       @{
      -  register char *s;
      +  register size_t i;
         (void) context;
      
         /* Get the offset in the path where this path info starts.  */
      @@@@ -479,20 +505,35 @@@@ visit_old_format(struct process_data *pr
           procdata->count += getw (procdata->fp) - LOCATEDB_OLD_OFFSET;
         else
           procdata->count += procdata->c - LOCATEDB_OLD_OFFSET;
      +  assert(procdata->count > 0);
      
      -  /* Overlay the old path with the remainder of the new.  */
      -  for (s = procdata->original_filename + procdata->count;
      +  /* Overlay the old path with the remainder of the new.  Read
      +   * more data until we get to the next filename.
      +   */
      +  for (i=procdata->count;
              (procdata->c = getc (procdata->fp)) > LOCATEDB_OLD_ESCAPE;)
      -    if (procdata->c < 0200)
      -      *s++ = procdata->c;		/* An ordinary character.  */
      -    else
      -      @{
      -	/* Bigram markers have the high bit set. */
      -	procdata->c &= 0177;
      -	*s++ = procdata->bigram1[procdata->c];
      -	*s++ = procdata->bigram2[procdata->c];
      -      @}
      -  *s-- = '\0';
      +    @{
      +      if (procdata->c < 0200)
      +	@{
      +	  /* An ordinary character. */
      +	  extend (procdata, i, 1u);
      +	  procdata->original_filename[i++] = procdata->c;
      +	@}
      +      else
      +	@{
      +	  /* Bigram markers have the high bit set. */
      +	  extend (procdata, i, 2u);
      +	  procdata->c &= 0177;
      +	  procdata->original_filename[i++] = procdata->bigram1[procdata->c];
      +	  procdata->original_filename[i++] = procdata->bigram2[procdata->c];
      +	@}
      +    @}
      +
      +  /* Consider the case where we executed the loop body zero times; we
      +   * still need space for the terminating null byte.
      +   */
      +  extend (procdata, i, 1u);
      +  procdata->original_filename[i] = 0;
      
         procdata->munged_filename = procdata->original_filename;
 
 
      VIII. THANKS
      ============
 
      Thanks to Rob Holland <rob@inversepath.com> and Tavis Ormandy.
 
 
      VIII. CVE INFORMATION
      =====================
 
      No CVE candidate number has yet been assigned for this vulnerability.
      If someone provides one, I will include it in the public announcement
      and change logs.
 
    The original announcement above was sent out with a cleartext PGP
 signature, of course, but that has been omitted from the example.
 
    Once a fixed release is available, announce the new release using the
 normal channels.  Any CVE number assigned for the problem should be
 included in the 'ChangeLog' and 'NEWS' entries.  See
 <https://cve.mitre.org/> for an explanation of CVE numbers.