find-maint: Security

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