unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Paul Eggert <eggert@cs.ucla.edu>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 12632@debbugs.gnu.org
Subject: bug#12632: file permissions checking mishandled when setuid
Date: Sat, 13 Oct 2012 23:16:44 -0700	[thread overview]
Message-ID: <507A58CC.10209@cs.ucla.edu> (raw)
In-Reply-To: <83a9vq7oqh.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 971 bytes --]

On 10/13/2012 12:23 AM, Eli Zaretskii wrote:

> The FIXMEs are OK, but I see no reason for them to come _instead_ of
> comments which explain why 'access' is used instead of 'stat'.

OK, I left those comments alone in the attached revision
of the patch.

> How will the new code work if 'dir' is nil?

'dir' can't be nil there.  But this is a separate issue, so I've
omitted that change from the revised patch.

> Also, what about lread.c:openp, around line 1555: doesn't it want
> 'euidaccess' as well, rather than 'stat'?

Sure, we can do that.  Done in the revised patch.

> I don't understand why is it a good idea to use 'euidaccess' in
> check_existing.  Isn't the fact of the mere existence of a file
> independent of user's access rights?

No, because you cannot even stat a file that's in a directory that you
can't search.  Using 'access' rather than 'euidaccess' might
let a setuid Emacs search directories that it shouldn't be able
to search, or vice versa.


[-- Attachment #2: euidaccess.txt --]
[-- Type: text/plain, Size: 47247 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2012-10-11 11:29:47 +0000
+++ ChangeLog	2012-10-14 06:03:50 +0000
@@ -1,3 +1,12 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* configure.ac (euidaccess): Remove check; gnulib does this for us now.
+	* lib/euidaccess.c, lib/getgroups.c, lib/group-member.c:
+	* lib/root-uid.h, lib/xalloc-oversized.h, m4/euidaccess.m4:
+	* m4/getgroups.m4, m4/group-member.m4: New files, from gnulib.
+	* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* .bzrignore: Add several files under admin/charsets.

=== modified file 'admin/ChangeLog'
--- admin/ChangeLog	2012-10-11 11:29:47 +0000
+++ admin/ChangeLog	2012-10-14 06:03:50 +0000
@@ -1,3 +1,9 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* merge-gnulib (GNULIB_MODULES): Add euidaccess.
+	(GNULIB_TOOL_FLAGS): Avoid malloc-posix.
+
 2012-10-11  Kenichi Handa  <handa@gnu.org>
 
 	* charsets/mapconv: Adjusted for the change of mapfiles/*.gz to

=== modified file 'admin/merge-gnulib'
--- admin/merge-gnulib	2012-09-27 01:06:23 +0000
+++ admin/merge-gnulib	2012-10-12 23:28:31 +0000
@@ -28,7 +28,7 @@
 GNULIB_MODULES='
   alloca-opt c-ctype c-strcase
   careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512
-  dtoastr dtotimespec dup2 environ execinfo
+  dtoastr dtotimespec dup2 environ euidaccess execinfo
   filemode getloadavg getopt-gnu gettime gettimeofday
   ignore-value intprops largefile lstat
   manywarnings mktime pselect pthread_sigmask readlink
@@ -40,7 +40,7 @@
 
 GNULIB_TOOL_FLAGS='
   --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat
-  --avoid=msvc-inval --avoid=msvc-nothrow
+  --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow
   --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types
   --avoid=threadlib
   --conditional-dependencies --import --no-changelog --no-vc-files

=== modified file 'configure.ac'
--- configure.ac	2012-10-08 16:09:00 +0000
+++ configure.ac	2012-10-12 23:28:31 +0000
@@ -2872,7 +2872,7 @@
 AC_CHECK_FUNCS(gethostname \
 closedir getrusage get_current_dir_name \
 lrand48 setsid \
-fpathconf select euidaccess getpagesize setlocale \
+fpathconf select getpagesize setlocale \
 utimes getrlimit setrlimit setpgid getcwd shutdown getaddrinfo \
 __fpending strsignal setitimer \
 sendto recvfrom getsockname getpeername getifaddrs freeifaddrs \

=== added file 'lib/euidaccess.c'
--- lib/euidaccess.c	1970-01-01 00:00:00 +0000
+++ lib/euidaccess.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,221 @@
+/* euidaccess -- check if effective user id can access file
+
+   Copyright (C) 1990-1991, 1995, 1998, 2000, 2003-2006, 2008-2012 Free
+   Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by David MacKenzie and Torbjorn Granlund.
+   Adapted for GNU C library by Roland McGrath.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "root-uid.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#if defined EACCES && !defined EACCESS
+# define EACCESS EACCES
+#endif
+
+#ifndef F_OK
+# define F_OK 0
+# define X_OK 1
+# define W_OK 2
+# define R_OK 4
+#endif
+
+
+#ifdef _LIBC
+
+# define access __access
+# define getuid __getuid
+# define getgid __getgid
+# define geteuid __geteuid
+# define getegid __getegid
+# define group_member __group_member
+# define euidaccess __euidaccess
+# undef stat
+# define stat stat64
+
+#endif
+
+/* Return 0 if the user has permission of type MODE on FILE;
+   otherwise, return -1 and set 'errno'.
+   Like access, except that it uses the effective user and group
+   id's instead of the real ones, and it does not always check for read-only
+   file system, text busy, etc.  */
+
+int
+euidaccess (const char *file, int mode)
+{
+#if HAVE_FACCESSAT                   /* glibc, AIX 7, Solaris 11, Cygwin 1.7 */
+  return faccessat (AT_FDCWD, file, mode, AT_EACCESS);
+#elif defined EFF_ONLY_OK               /* IRIX, OSF/1, Interix */
+  return access (file, mode | EFF_ONLY_OK);
+#elif defined ACC_SELF                  /* AIX */
+  return accessx (file, mode, ACC_SELF);
+#elif HAVE_EACCESS                      /* FreeBSD */
+  return eaccess (file, mode);
+#else       /* Mac OS X, NetBSD, OpenBSD, HP-UX, Solaris, Cygwin, mingw, BeOS */
+
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
+  uid_t euid = geteuid ();
+  gid_t egid = getegid ();
+  struct stat stats;
+
+# if HAVE_DECL_SETREGID && PREFER_NONREENTRANT_EUIDACCESS
+
+  /* Define PREFER_NONREENTRANT_EUIDACCESS if you prefer euidaccess to
+     return the correct result even if this would make it
+     nonreentrant.  Define this only if your entire application is
+     safe even if the uid or gid might temporarily change.  If your
+     application uses signal handlers or threads it is probably not
+     safe.  */
+
+  if (mode == F_OK)
+    return stat (file, &stats);
+  else
+    {
+      int result;
+      int saved_errno;
+
+      if (uid != euid)
+        setreuid (euid, uid);
+      if (gid != egid)
+        setregid (egid, gid);
+
+      result = access (file, mode);
+      saved_errno = errno;
+
+      /* Restore them.  */
+      if (uid != euid)
+        setreuid (uid, euid);
+      if (gid != egid)
+        setregid (gid, egid);
+
+      errno = saved_errno;
+      return result;
+    }
+
+# else
+
+  /* The following code assumes the traditional Unix model, and is not
+     correct on systems that have ACLs or the like.  However, it's
+     better than nothing, and it is reentrant.  */
+
+  unsigned int granted;
+  if (uid == euid && gid == egid)
+    /* If we are not set-uid or set-gid, access does the same.  */
+    return access (file, mode);
+
+  if (stat (file, &stats) != 0)
+    return -1;
+
+  /* The super-user can read and write any file, and execute any file
+     that anyone can execute.  */
+  if (euid == ROOT_UID
+      && ((mode & X_OK) == 0
+          || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
+    return 0;
+
+  /* Convert the mode to traditional form, clearing any bogus bits.  */
+  if (R_OK == 4 && W_OK == 2 && X_OK == 1 && F_OK == 0)
+    mode &= 7;
+  else
+    mode = ((mode & R_OK ? 4 : 0)
+            + (mode & W_OK ? 2 : 0)
+            + (mode & X_OK ? 1 : 0));
+
+  if (mode == 0)
+    return 0;                   /* The file exists.  */
+
+  /* Convert the file's permission bits to traditional form.  */
+  if (S_IRUSR == (4 << 6) && S_IWUSR == (2 << 6) && S_IXUSR == (1 << 6)
+      && S_IRGRP == (4 << 3) && S_IWGRP == (2 << 3) && S_IXGRP == (1 << 3)
+      && S_IROTH == (4 << 0) && S_IWOTH == (2 << 0) && S_IXOTH == (1 << 0))
+    granted = stats.st_mode;
+  else
+    granted = ((stats.st_mode & S_IRUSR ? 4 << 6 : 0)
+               + (stats.st_mode & S_IWUSR ? 2 << 6 : 0)
+               + (stats.st_mode & S_IXUSR ? 1 << 6 : 0)
+               + (stats.st_mode & S_IRGRP ? 4 << 3 : 0)
+               + (stats.st_mode & S_IWGRP ? 2 << 3 : 0)
+               + (stats.st_mode & S_IXGRP ? 1 << 3 : 0)
+               + (stats.st_mode & S_IROTH ? 4 << 0 : 0)
+               + (stats.st_mode & S_IWOTH ? 2 << 0 : 0)
+               + (stats.st_mode & S_IXOTH ? 1 << 0 : 0));
+
+  if (euid == stats.st_uid)
+    granted >>= 6;
+  else if (egid == stats.st_gid || group_member (stats.st_gid))
+    granted >>= 3;
+
+  if ((mode & ~granted) == 0)
+    return 0;
+  __set_errno (EACCESS);
+  return -1;
+
+# endif
+#endif
+}
+#undef euidaccess
+#ifdef weak_alias
+weak_alias (__euidaccess, euidaccess)
+#endif
+\f
+#ifdef TEST
+# include <error.h>
+# include <stdio.h>
+# include <stdlib.h>
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  char *file;
+  int mode;
+  int err;
+
+  program_name = argv[0];
+  if (argc < 3)
+    abort ();
+  file = argv[1];
+  mode = atoi (argv[2]);
+
+  err = euidaccess (file, mode);
+  printf ("%d\n", err);
+  if (err != 0)
+    error (0, errno, "%s", file);
+  exit (0);
+}
+#endif

=== added file 'lib/getgroups.c'
--- lib/getgroups.c	1970-01-01 00:00:00 +0000
+++ lib/getgroups.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,116 @@
+/* provide consistent interface to getgroups for systems that don't allow N==0
+
+   Copyright (C) 1996, 1999, 2003, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if !HAVE_GETGROUPS
+
+/* Provide a stub that fails with ENOSYS, since there is no group
+   information available on mingw.  */
+int
+getgroups (int n _GL_UNUSED, GETGROUPS_T *groups _GL_UNUSED)
+{
+  errno = ENOSYS;
+  return -1;
+}
+
+#else /* HAVE_GETGROUPS */
+
+# undef getgroups
+# ifndef GETGROUPS_ZERO_BUG
+#  define GETGROUPS_ZERO_BUG 0
+# endif
+
+/* On at least Ultrix 4.3 and NextStep 3.2, getgroups (0, NULL) always
+   fails.  On other systems, it returns the number of supplemental
+   groups for the process.  This function handles that special case
+   and lets the system-provided function handle all others.  However,
+   it can fail with ENOMEM if memory is tight.  It is unspecified
+   whether the effective group id is included in the list.  */
+
+int
+rpl_getgroups (int n, gid_t *group)
+{
+  int n_groups;
+  GETGROUPS_T *gbuf;
+  int saved_errno;
+
+  if (n < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (n != 0 || !GETGROUPS_ZERO_BUG)
+    {
+      int result;
+      if (sizeof *group == sizeof *gbuf)
+        return getgroups (n, (GETGROUPS_T *) group);
+
+      if (SIZE_MAX / sizeof *gbuf <= n)
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      result = getgroups (n, gbuf);
+      if (0 <= result)
+        {
+          n = result;
+          while (n--)
+            group[n] = gbuf[n];
+        }
+      saved_errno = errno;
+      free (gbuf);
+      errno == saved_errno;
+      return result;
+    }
+
+  n = 20;
+  while (1)
+    {
+      /* No need to worry about address arithmetic overflow here,
+         since the ancient systems that we're running on have low
+         limits on the number of secondary groups.  */
+      gbuf = malloc (n * sizeof *gbuf);
+      if (!gbuf)
+        return -1;
+      n_groups = getgroups (n, gbuf);
+      if (n_groups == -1 ? errno != EINVAL : n_groups < n)
+        break;
+      free (gbuf);
+      n *= 2;
+    }
+
+  saved_errno = errno;
+  free (gbuf);
+  errno = saved_errno;
+
+  return n_groups;
+}
+
+#endif /* HAVE_GETGROUPS */

=== modified file 'lib/gnulib.mk'
--- lib/gnulib.mk	2012-10-04 07:15:42 +0000
+++ lib/gnulib.mk	2012-10-12 23:28:31 +0000
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
+# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=errno --avoid=fcntl --avoid=fcntl-h --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=raise --avoid=select --avoid=sigprocmask --avoid=sys_types --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt c-ctype c-strcase careadlinkat crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ euidaccess execinfo filemode getloadavg getopt-gnu gettime gettimeofday ignore-value intprops largefile lstat manywarnings mktime pselect pthread_sigmask readlink socklen stat-time stdalign stdarg stdbool stdio strftime strtoimax strtoumax symlink sys_stat sys_time time timer-time timespec-add timespec-sub utimens warnings
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -150,6 +150,15 @@
 
 ## end   gnulib module dup2
 
+## begin gnulib module euidaccess
+
+
+EXTRA_DIST += euidaccess.c
+
+EXTRA_libgnu_a_SOURCES += euidaccess.c
+
+## end   gnulib module euidaccess
+
 ## begin gnulib module execinfo
 
 BUILT_SOURCES += $(EXECINFO_H)
@@ -183,6 +192,17 @@
 
 ## end   gnulib module filemode
 
+## begin gnulib module getgroups
+
+if gl_GNULIB_ENABLED_getgroups
+
+endif
+EXTRA_DIST += getgroups.c
+
+EXTRA_libgnu_a_SOURCES += getgroups.c
+
+## end   gnulib module getgroups
+
 ## begin gnulib module getloadavg
 
 
@@ -242,6 +262,17 @@
 
 ## end   gnulib module gettimeofday
 
+## begin gnulib module group-member
+
+if gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1
+
+endif
+EXTRA_DIST += group-member.c
+
+EXTRA_libgnu_a_SOURCES += group-member.c
+
+## end   gnulib module group-member
+
 ## begin gnulib module ignore-value
 
 
@@ -354,6 +385,13 @@
 
 ## end   gnulib module readlink
 
+## begin gnulib module root-uid
+
+
+EXTRA_DIST += root-uid.h
+
+## end   gnulib module root-uid
+
 ## begin gnulib module signal-h
 
 BUILT_SOURCES += signal.h
@@ -1312,6 +1350,15 @@
 
 ## end   gnulib module verify
 
+## begin gnulib module xalloc-oversized
+
+if gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec
+
+endif
+EXTRA_DIST += xalloc-oversized.h
+
+## end   gnulib module xalloc-oversized
+
 
 mostlyclean-local: mostlyclean-generic
 	@for dir in '' $(MOSTLYCLEANDIRS); do \

=== added file 'lib/group-member.c'
--- lib/group-member.c	1970-01-01 00:00:00 +0000
+++ lib/group-member.c	2012-10-12 23:28:31 +0000
@@ -0,0 +1,119 @@
+/* group-member.c -- determine whether group id is in calling user's group list
+
+   Copyright (C) 1994, 1997-1998, 2003, 2005-2006, 2009-2012 Free Software
+   Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "xalloc-oversized.h"
+
+/* Most processes have no more than this many groups, and for these
+   processes we can avoid using malloc.  */
+enum { GROUPBUF_SIZE = 100 };
+
+struct group_info
+  {
+    gid_t *group;
+    gid_t groupbuf[GROUPBUF_SIZE];
+  };
+
+static void
+free_group_info (struct group_info const *g)
+{
+  if (g->group != g->groupbuf)
+    free (g->group);
+}
+
+static int
+get_group_info (struct group_info *gi)
+{
+  int n_groups = getgroups (GROUPBUF_SIZE, gi->groupbuf);
+  gi->group = gi->groupbuf;
+
+  if (n_groups < 0)
+    {
+      int n_group_slots = getgroups (0, NULL);
+      if (0 <= n_group_slots
+          && ! xalloc_oversized (n_group_slots, sizeof *gi->group))
+        {
+          gi->group = malloc (n_group_slots * sizeof *gi->group);
+          if (gi->group)
+            n_groups = getgroups (n_group_slots, gi->group);
+        }
+    }
+
+  /* In case of error, the user loses.  */
+  return n_groups;
+}
+
+/* Return non-zero if GID is one that we have in our groups list.
+   Note that the groups list is not guaranteed to contain the current
+   or effective group ID, so they should generally be checked
+   separately.  */
+
+int
+group_member (gid_t gid)
+{
+  int i;
+  int found;
+  struct group_info gi;
+  int n_groups = get_group_info (&gi);
+
+  /* Search through the list looking for GID. */
+  found = 0;
+  for (i = 0; i < n_groups; i++)
+    {
+      if (gid == gi.group[i])
+        {
+          found = 1;
+          break;
+        }
+    }
+
+  free_group_info (&gi);
+
+  return found;
+}
+
+#ifdef TEST
+
+char *program_name;
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  program_name = argv[0];
+
+  for (i = 1; i < argc; i++)
+    {
+      gid_t gid;
+
+      gid = atoi (argv[i]);
+      printf ("%d: %s\n", gid, group_member (gid) ? "yes" : "no");
+    }
+  exit (0);
+}
+
+#endif /* TEST */

=== added file 'lib/root-uid.h'
--- lib/root-uid.h	1970-01-01 00:00:00 +0000
+++ lib/root-uid.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,30 @@
+/* The user ID that always has appropriate privileges in the POSIX sense.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Written by Paul Eggert.  */
+
+#ifndef ROOT_UID_H_
+#define ROOT_UID_H_
+
+/* The user ID that always has appropriate privileges in the POSIX sense.  */
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+
+#endif

=== added file 'lib/xalloc-oversized.h'
--- lib/xalloc-oversized.h	1970-01-01 00:00:00 +0000
+++ lib/xalloc-oversized.h	2012-10-12 23:28:31 +0000
@@ -0,0 +1,38 @@
+/* xalloc-oversized.h -- memory allocation size checking
+
+   Copyright (C) 1990-2000, 2003-2004, 2006-2012 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef XALLOC_OVERSIZED_H_
+# define XALLOC_OVERSIZED_H_
+
+# include <stddef.h>
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not a function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.  */
+# define xalloc_oversized(n, s) \
+    ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+#endif /* !XALLOC_OVERSIZED_H_ */

=== added file 'm4/euidaccess.m4'
--- m4/euidaccess.m4	1970-01-01 00:00:00 +0000
+++ m4/euidaccess.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,52 @@
+# euidaccess.m4 serial 14
+dnl Copyright (C) 2002-2012 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_NONREENTRANT_EUIDACCESS],
+[
+  AC_REQUIRE([gl_FUNC_EUIDACCESS])
+  AC_DEFINE([PREFER_NONREENTRANT_EUIDACCESS], [1],
+    [Define this if you prefer euidaccess to return the correct result
+     even if this would make it nonreentrant.  Define this only if your
+     entire application is safe even if the uid or gid might temporarily
+     change.  If your application uses signal handlers or threads it
+     is probably not safe.])
+])
+
+AC_DEFUN([gl_FUNC_EUIDACCESS],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare euidaccess().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_FUNCS([euidaccess])
+  if test $ac_cv_func_euidaccess = no; then
+    HAVE_EUIDACCESS=0
+  fi
+])
+
+# Prerequisites of lib/euidaccess.c.
+AC_DEFUN([gl_PREREQ_EUIDACCESS], [
+  dnl Prefer POSIX faccessat over non-standard euidaccess.
+  AC_CHECK_FUNCS_ONCE([faccessat])
+  dnl Try various other non-standard fallbacks.
+  AC_CHECK_HEADERS_ONCE([libgen.h])
+  AC_CHECK_DECLS_ONCE([setregid])
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+
+  # Solaris 9 and 10 need -lgen to get the eaccess function.
+  # Save and restore LIBS so -lgen isn't added to it.  Otherwise, *all*
+  # programs in the package would end up linked with that potentially-shared
+  # library, inducing unnecessary run-time overhead.
+  LIB_EACCESS=
+  AC_SUBST([LIB_EACCESS])
+  gl_saved_libs=$LIBS
+    AC_SEARCH_LIBS([eaccess], [gen],
+                   [test "$ac_cv_search_eaccess" = "none required" ||
+                    LIB_EACCESS=$ac_cv_search_eaccess])
+    AC_CHECK_FUNCS([eaccess])
+  LIBS=$gl_saved_libs
+])

=== added file 'm4/getgroups.m4'
--- m4/getgroups.m4	1970-01-01 00:00:00 +0000
+++ m4/getgroups.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,107 @@
+# serial 18
+
+dnl From Jim Meyering.
+dnl A wrapper around AC_FUNC_GETGROUPS.
+
+# Copyright (C) 1996-1997, 1999-2004, 2008-2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+m4_version_prereq([2.70], [] ,[
+
+# This is taken from the following Autoconf patch:
+# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=commitdiff;h=7fbb553727ed7e0e689a17594b58559ecf3ea6e9
+AC_DEFUN([AC_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])dnl
+  AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+  AC_REQUIRE([AC_CANONICAL_HOST])dnl for cross-compiles
+  AC_CHECK_FUNC([getgroups])
+
+  # If we don't yet have getgroups, see if it's in -lbsd.
+  # This is reported to be necessary on an ITOS 3000WS running SEIUX 3.1.
+  ac_save_LIBS=$LIBS
+  if test $ac_cv_func_getgroups = no; then
+    AC_CHECK_LIB(bsd, getgroups, [GETGROUPS_LIB=-lbsd])
+  fi
+
+  # Run the program to test the functionality of the system-supplied
+  # getgroups function only if there is such a function.
+  if test $ac_cv_func_getgroups = yes; then
+    AC_CACHE_CHECK([for working getgroups], [ac_cv_func_getgroups_works],
+      [AC_RUN_IFELSE(
+         [AC_LANG_PROGRAM(
+            [AC_INCLUDES_DEFAULT],
+            [[/* On Ultrix 4.3, getgroups (0, 0) always fails.  */
+              return getgroups (0, 0) == -1;]])
+         ],
+         [ac_cv_func_getgroups_works=yes],
+         [ac_cv_func_getgroups_works=no],
+         [case "$host_os" in # ((
+                    # Guess yes on glibc systems.
+            *-gnu*) ac_cv_func_getgroups_works="guessing yes" ;;
+                    # If we don't know, assume the worst.
+            *)      ac_cv_func_getgroups_works="guessing no" ;;
+          esac
+         ])
+      ])
+  else
+    ac_cv_func_getgroups_works=no
+  fi
+  case "$ac_cv_func_getgroups_works" in
+    *yes)
+      AC_DEFINE([HAVE_GETGROUPS], [1],
+        [Define to 1 if your system has a working `getgroups' function.])
+      ;;
+  esac
+  LIBS=$ac_save_LIBS
+])# AC_FUNC_GETGROUPS
+
+])
+
+AC_DEFUN([gl_FUNC_GETGROUPS],
+[
+  AC_REQUIRE([AC_TYPE_GETGROUPS])
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  AC_FUNC_GETGROUPS
+  if test $ac_cv_func_getgroups != yes; then
+    HAVE_GETGROUPS=0
+  else
+    if test "$ac_cv_type_getgroups" != gid_t \
+       || { case "$ac_cv_func_getgroups_works" in
+              *yes) false;;
+              *) true;;
+            esac
+          }; then
+      REPLACE_GETGROUPS=1
+      AC_DEFINE([GETGROUPS_ZERO_BUG], [1], [Define this to 1 if
+        getgroups(0,NULL) does not return the number of groups.])
+    else
+      dnl Detect FreeBSD bug; POSIX requires getgroups(-1,ptr) to fail.
+      AC_CACHE_CHECK([whether getgroups handles negative values],
+        [gl_cv_func_getgroups_works],
+        [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+          [[int size = getgroups (0, 0);
+            gid_t *list = malloc (size * sizeof *list);
+            return getgroups (-1, list) != -1;]])],
+          [gl_cv_func_getgroups_works=yes],
+          [gl_cv_func_getgroups_works=no],
+          [case "$host_os" in
+                     # Guess yes on glibc systems.
+             *-gnu*) gl_cv_func_getgroups_works="guessing yes" ;;
+                     # If we don't know, assume the worst.
+             *)      gl_cv_func_getgroups_works="guessing no" ;;
+           esac
+          ])])
+      case "$gl_cv_func_getgroups_works" in
+        *yes) ;;
+        *) REPLACE_GETGROUPS=1 ;;
+      esac
+    fi
+  fi
+  test -n "$GETGROUPS_LIB" && LIBS="$GETGROUPS_LIB $LIBS"
+])

=== modified file 'm4/gnulib-comp.m4'
--- m4/gnulib-comp.m4	2012-09-27 01:06:23 +0000
+++ m4/gnulib-comp.m4	2012-10-12 23:28:31 +0000
@@ -53,17 +53,20 @@
   # Code from module dtotimespec:
   # Code from module dup2:
   # Code from module environ:
+  # Code from module euidaccess:
   # Code from module execinfo:
   # Code from module extensions:
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   # Code from module extern-inline:
   # Code from module filemode:
+  # Code from module getgroups:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
   # Code from module gettext-h:
   # Code from module gettime:
   # Code from module gettimeofday:
+  # Code from module group-member:
   # Code from module ignore-value:
   # Code from module include_next:
   # Code from module intprops:
@@ -79,6 +82,7 @@
   # Code from module pselect:
   # Code from module pthread_sigmask:
   # Code from module readlink:
+  # Code from module root-uid:
   # Code from module signal-h:
   # Code from module snippet/_Noreturn:
   # Code from module snippet/arg-nonnull:
@@ -120,6 +124,7 @@
   # Code from module utimens:
   # Code from module verify:
   # Code from module warnings:
+  # Code from module xalloc-oversized:
 ])
 
 # This macro should be invoked from ./configure.ac, in the section
@@ -154,6 +159,12 @@
   gl_UNISTD_MODULE_INDICATOR([dup2])
   gl_ENVIRON
   gl_UNISTD_MODULE_INDICATOR([environ])
+  gl_FUNC_EUIDACCESS
+  if test $HAVE_EUIDACCESS = 0; then
+    AC_LIBOBJ([euidaccess])
+    gl_PREREQ_EUIDACCESS
+  fi
+  gl_UNISTD_MODULE_INDICATOR([euidaccess])
   gl_EXECINFO_H
   AC_REQUIRE([gl_EXTERN_INLINE])
   gl_FILEMODE
@@ -269,18 +280,32 @@
   gl_UNISTD_H
   gl_UTIMENS
   gl_gnulib_enabled_dosname=false
+  gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
+  gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_stat=false
   gl_gnulib_enabled_strtoll=false
   gl_gnulib_enabled_strtoull=false
   gl_gnulib_enabled_verify=false
+  gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
   func_gl_gnulib_m4code_dosname ()
   {
     if ! $gl_gnulib_enabled_dosname; then
       gl_gnulib_enabled_dosname=true
     fi
   }
+  func_gl_gnulib_m4code_getgroups ()
+  {
+    if ! $gl_gnulib_enabled_getgroups; then
+      gl_FUNC_GETGROUPS
+      if test $HAVE_GETGROUPS = 0 || test $REPLACE_GETGROUPS = 1; then
+        AC_LIBOBJ([getgroups])
+      fi
+      gl_UNISTD_MODULE_INDICATOR([getgroups])
+      gl_gnulib_enabled_getgroups=true
+    fi
+  }
   func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 ()
   {
     if ! $gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36; then
@@ -289,6 +314,24 @@
       gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=true
     fi
   }
+  func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1 ()
+  {
+    if ! $gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1; then
+      gl_FUNC_GROUP_MEMBER
+      if test $HAVE_GROUP_MEMBER = 0; then
+        AC_LIBOBJ([group-member])
+        gl_PREREQ_GROUP_MEMBER
+      fi
+      gl_UNISTD_MODULE_INDICATOR([group-member])
+      gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=true
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_getgroups
+      fi
+      if test $HAVE_GROUP_MEMBER = 0; then
+        func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec
+      fi
+    fi
+  }
   func_gl_gnulib_m4code_pathmax ()
   {
     if ! $gl_gnulib_enabled_pathmax; then
@@ -347,6 +390,18 @@
       gl_gnulib_enabled_verify=true
     fi
   }
+  func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
+  {
+    if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then
+      gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
+    fi
+  }
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_a9786850e999ae65a836a6041e8e5ed1
+  fi
+  if test $HAVE_EUIDACCESS = 0; then
+    func_gl_gnulib_m4code_stat
+  fi
   if test $REPLACE_GETOPT = 1; then
     func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36
   fi
@@ -373,12 +428,15 @@
   fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dosname], [$gl_gnulib_enabled_dosname])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_stat], [$gl_gnulib_enabled_stat])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoull], [$gl_gnulib_enabled_strtoull])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_verify], [$gl_gnulib_enabled_verify])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
   # End of code from modules
   m4_ifval(gl_LIBSOURCES_LIST, [
     m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -538,12 +596,14 @@
   lib/dtoastr.c
   lib/dtotimespec.c
   lib/dup2.c
+  lib/euidaccess.c
   lib/execinfo.c
   lib/execinfo.in.h
   lib/filemode.c
   lib/filemode.h
   lib/ftoastr.c
   lib/ftoastr.h
+  lib/getgroups.c
   lib/getloadavg.c
   lib/getopt.c
   lib/getopt.in.h
@@ -552,6 +612,7 @@
   lib/gettext.h
   lib/gettime.c
   lib/gettimeofday.c
+  lib/group-member.c
   lib/ignore-value.h
   lib/intprops.h
   lib/inttypes.in.h
@@ -564,6 +625,7 @@
   lib/pselect.c
   lib/pthread_sigmask.c
   lib/readlink.c
+  lib/root-uid.h
   lib/sha1.c
   lib/sha1.h
   lib/sha256.c
@@ -605,21 +667,25 @@
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
+  lib/xalloc-oversized.h
   m4/00gnulib.m4
   m4/alloca.m4
   m4/c-strtod.m4
   m4/clock_time.m4
   m4/dup2.m4
   m4/environ.m4
+  m4/euidaccess.m4
   m4/execinfo.m4
   m4/extensions.m4
   m4/extern-inline.m4
   m4/filemode.m4
+  m4/getgroups.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/gettime.m4
   m4/gettimeofday.m4
   m4/gnulib-common.m4
+  m4/group-member.m4
   m4/include_next.m4
   m4/inttypes.m4
   m4/largefile.m4

=== added file 'm4/group-member.m4'
--- m4/group-member.m4	1970-01-01 00:00:00 +0000
+++ m4/group-member.m4	2012-10-12 23:28:31 +0000
@@ -0,0 +1,29 @@
+# serial 14
+
+# Copyright (C) 1999-2001, 2003-2007, 2009-2012 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+dnl Written by Jim Meyering
+
+AC_DEFUN([gl_FUNC_GROUP_MEMBER],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+
+  dnl Persuade glibc <unistd.h> to declare group_member().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  dnl Do this replacement check manually because I want the hyphen
+  dnl (not the underscore) in the filename.
+  AC_CHECK_FUNC([group_member], , [
+    HAVE_GROUP_MEMBER=0
+  ])
+])
+
+# Prerequisites of lib/group-member.c.
+AC_DEFUN([gl_PREREQ_GROUP_MEMBER],
+[
+  AC_REQUIRE([AC_FUNC_GETGROUPS])
+])

=== modified file 'src/ChangeLog'
--- src/ChangeLog	2012-10-14 00:17:07 +0000
+++ src/ChangeLog	2012-10-14 06:02:52 +0000
@@ -1,3 +1,27 @@
+2012-10-14  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Use euidaccess, not access, when checking file permissions (Bug#12632).
+	* Makefile.in (LIB_EACCESS): New macro.
+	(LIBES): Use it.
+	* callproc.c (init_callproc):
+	* charset.c (init_charset):
+	* fileio.c (check_existing) [!DOS_NT]:
+	(check_executable) [!DOS_NT && !HAVE_EUIDACCESS]:
+	* lread.c (openp, load_path_check):
+	* process.c (allocate_pty):
+	* xrdb.c (file_p):
+	Use euidaccess, not access.  Use symbolic names instead of integers
+	for the flags, as they're portable now.
+	* fileio.c (Ffile_readable_p) [!DOS_NT && !macintosh]:
+	Use euidaccess, not stat + open + close.
+	(file_directory_p): New function, which uses 'stat' on most places
+	but 'access' (for efficiency) if WINDOWSNT.
+	(Ffile_directory_p): Use it.
+	* lisp.h (file_directory_p): New decl.
+	* lread.c (openp): When opening a file, use fstat rather than
+	stat, as that avoids a permissions race.  When not opening a file,
+	use file_directory_p rather than stat.
+
 2012-10-13  Jan Djärv  <jan.h.d@swipnet.se>
 
 	* gtkutil.c (xg_set_widget_bg): Divide by 65535 (Bug#12612).

=== modified file 'src/Makefile.in'
--- src/Makefile.in	2012-10-08 12:53:18 +0000
+++ src/Makefile.in	2012-10-13 01:49:41 +0000
@@ -150,6 +150,7 @@
 M17N_FLT_LIBS = @M17N_FLT_LIBS@
 
 LIB_CLOCK_GETTIME=@LIB_CLOCK_GETTIME@
+LIB_EACCESS=@LIB_EACCESS@
 LIB_TIMER_TIME=@LIB_TIMER_TIME@
 
 DBUS_CFLAGS = @DBUS_CFLAGS@
@@ -392,7 +393,7 @@
 LIBES = $(LIBS) $(W32_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_CLOCK_GETTIME) \
-   $(LIB_TIMER_TIME) $(DBUS_LIBS) \
+   $(LIB_EACCESS) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBRESOLV) $(LIBS_SYSTEM) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \

=== modified file 'src/callproc.c'
--- src/callproc.c	2012-09-23 22:25:22 +0000
+++ src/callproc.c	2012-10-12 23:28:31 +0000
@@ -1597,13 +1597,13 @@
 #endif
     {
       tempdir = Fdirectory_file_name (Vexec_directory);
-      if (access (SSDATA (tempdir), 0) < 0)
+      if (euidaccess (SSDATA (tempdir), F_OK) < 0)
 	dir_warning ("Warning: arch-dependent data dir (%s) does not exist.\n",
 		     Vexec_directory);
     }
 
   tempdir = Fdirectory_file_name (Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) < 0)
     dir_warning ("Warning: arch-independent data dir (%s) does not exist.\n",
 		 Vdata_directory);
 

=== modified file 'src/charset.c'
--- src/charset.c	2012-10-01 06:36:54 +0000
+++ src/charset.c	2012-10-12 23:28:31 +0000
@@ -2293,7 +2293,7 @@
 {
   Lisp_Object tempdir;
   tempdir = Fexpand_file_name (build_string ("charsets"), Vdata_directory);
-  if (access (SSDATA (tempdir), 0) < 0)
+  if (euidaccess (SSDATA (tempdir), F_OK) != 0)
     {
       /* This used to be non-fatal (dir_warning), but it should not
          happen, and if it does sooner or later it will cause some

=== modified file 'src/fileio.c'
--- src/fileio.c	2012-10-13 08:55:26 +0000
+++ src/fileio.c	2012-10-14 06:02:52 +0000
@@ -2428,10 +2428,11 @@
   /* The full emulation of Posix 'stat' is too expensive on
      DOS/Windows, when all we want to know is whether the file exists.
      So we use 'access' instead, which is much more lightweight.  */
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   return (access (filename, F_OK) >= 0);
 #else
-  struct stat st;
-  return (stat (filename, &st) >= 0);
+  return euidaccess (filename, F_OK) == 0;
 #endif
 }
 
@@ -2441,19 +2442,14 @@
 check_executable (char *filename)
 {
 #ifdef DOS_NT
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return ((st.st_mode & S_IEXEC) != 0);
 #else /* not DOS_NT */
-#ifdef HAVE_EUIDACCESS
-  return (euidaccess (filename, 1) >= 0);
-#else
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.  */
-  return (access (filename, 1) >= 0);
-#endif
+  return euidaccess (filename, X_OK) == 0;
 #endif /* not DOS_NT */
 }
 
@@ -2463,13 +2459,14 @@
 check_writable (const char *filename)
 {
 #ifdef MSDOS
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   struct stat st;
   if (stat (filename, &st) < 0)
     return 0;
   return (st.st_mode & S_IWRITE || S_ISDIR (st.st_mode));
 #else /* not MSDOS */
-#ifdef HAVE_EUIDACCESS
-  bool res = (euidaccess (filename, 2) >= 0);
+  bool res = euidaccess (filename, W_OK) == 0;
 #ifdef CYGWIN
   /* euidaccess may have returned failure because Cygwin couldn't
      determine the file's UID or GID; if so, we return success. */
@@ -2482,14 +2479,6 @@
     }
 #endif /* CYGWIN */
   return res;
-#else /* not HAVE_EUIDACCESS */
-  /* Access isn't quite right because it uses the real uid
-     and we really want to test with the effective uid.
-     But Unix doesn't give us a right way to do it.
-     Opening with O_WRONLY could work for an ordinary file,
-     but would lose for directories.  */
-  return (access (filename, 2) >= 0);
-#endif /* not HAVE_EUIDACCESS */
 #endif /* not MSDOS */
 }
 
@@ -2546,9 +2535,6 @@
 {
   Lisp_Object absname;
   Lisp_Object handler;
-  int desc;
-  int flags;
-  struct stat statbuf;
 
   CHECK_STRING (filename);
   absname = Fexpand_file_name (filename, Qnil);
@@ -2562,28 +2548,13 @@
   absname = ENCODE_FILE (absname);
 
 #if defined (DOS_NT) || defined (macintosh)
-  /* Under MS-DOS, Windows, and Macintosh, open does not work for
-     directories.  */
+  /* FIXME: an euidaccess implementation should be added to the
+     DOS/Windows ports and this #ifdef branch should be removed.  */
   if (access (SDATA (absname), 0) == 0)
     return Qt;
   return Qnil;
 #else /* not DOS_NT and not macintosh */
-  flags = O_RDONLY;
-#ifdef O_NONBLOCK
-  /* Opening a fifo without O_NONBLOCK can wait.
-     We don't want to wait.  But we don't want to mess wth O_NONBLOCK
-     except in the case of a fifo, on a system which handles it.  */
-  desc = stat (SSDATA (absname), &statbuf);
-  if (desc < 0)
-    return Qnil;
-  if (S_ISFIFO (statbuf.st_mode))
-    flags |= O_NONBLOCK;
-#endif
-  desc = emacs_open (SSDATA (absname), flags, 0);
-  if (desc < 0)
-    return Qnil;
-  emacs_close (desc);
-  return Qt;
+  return euidaccess (SSDATA (absname), R_OK) == 0 ? Qt : Qnil;
 #endif /* not DOS_NT and not macintosh */
 }
 
@@ -2702,8 +2673,7 @@
 See `file-symlink-p' to distinguish symlinks.  */)
   (Lisp_Object filename)
 {
-  register Lisp_Object absname;
-  struct stat st;
+  Lisp_Object absname;
   Lisp_Object handler;
 
   absname = expand_and_dir_to_file (filename, BVAR (current_buffer, directory));
@@ -2716,9 +2686,20 @@
 
   absname = ENCODE_FILE (absname);
 
-  if (stat (SSDATA (absname), &st) < 0)
-    return Qnil;
-  return S_ISDIR (st.st_mode) ? Qt : Qnil;
+  return file_directory_p (SSDATA (absname)) ? Qt : Qnil;
+}
+
+/* Return true if FILE is a directory or a symlink to a directory.  */
+bool
+file_directory_p (char const *file)
+{
+#ifdef WINDOWSNT
+  /* 'access' is cheaper than 'stat'.  */
+  return access (file, D_OK) == 0;
+#else
+  struct stat st;
+  return stat (file, &st) == 0 && S_ISDIR (st.st_mode);
+#endif
 }
 
 DEFUN ("file-accessible-directory-p", Ffile_accessible_directory_p,

=== modified file 'src/lisp.h'
--- src/lisp.h	2012-10-12 15:19:54 +0000
+++ src/lisp.h	2012-10-14 06:00:40 +0000
@@ -3179,6 +3179,7 @@
 extern Lisp_Object restore_point_unwind (Lisp_Object);
 extern _Noreturn void report_file_error (const char *, Lisp_Object);
 extern void internal_delete_file (Lisp_Object);
+extern bool file_directory_p (const char *);
 extern void syms_of_fileio (void);
 extern Lisp_Object make_temp_name (Lisp_Object, bool);
 extern Lisp_Object Qdelete_file;

=== modified file 'src/lread.c'
--- src/lread.c	2012-10-12 15:19:54 +0000
+++ src/lread.c	2012-10-14 06:00:40 +0000
@@ -1404,7 +1404,7 @@
 If SUFFIXES is non-nil, it should be a list of suffixes to append to
 file name when searching.
 If non-nil, PREDICATE is used instead of `file-readable-p'.
-PREDICATE can also be an integer to pass to the access(2) function,
+PREDICATE can also be an integer to pass to the euidaccess(2) function,
 in which case file-name-handlers are ignored.
 This function will normally skip directories, so if you want it to find
 directories, make sure the PREDICATE function returns `dir-ok' for them.  */)
@@ -1442,7 +1442,6 @@
 int
 openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate)
 {
-  int fd;
   ptrdiff_t fn_size = 100;
   char buf[100];
   char *fn = buf;
@@ -1497,7 +1496,6 @@
 	{
 	  ptrdiff_t fnlen, lsuffix = SBYTES (XCAR (tail));
 	  Lisp_Object handler;
-	  bool exists;
 
 	  /* Concatenate path element/specified name with the suffix.
 	     If the directory starts with /:, remove that.  */
@@ -1521,6 +1519,7 @@
 	  handler = Ffind_file_name_handler (string, Qfile_exists_p);
 	  if ((!NILP (handler) || !NILP (predicate)) && !NATNUMP (predicate))
             {
+	      bool exists;
 	      if (NILP (predicate))
 		exists = !NILP (Ffile_readable_p (string));
 	      else
@@ -1542,37 +1541,38 @@
 	    }
 	  else
 	    {
-#ifndef WINDOWSNT
-	      struct stat st;
-#endif
+	      int fd;
 	      const char *pfn;
 
 	      encoded_fn = ENCODE_FILE (string);
 	      pfn = SSDATA (encoded_fn);
-#ifdef WINDOWSNT
-	      exists = access (pfn, F_OK) == 0 && access (pfn, D_OK) < 0;
-#else
-	      exists = (stat (pfn, &st) == 0 && ! S_ISDIR (st.st_mode));
-#endif
-	      if (exists)
+
+	      /* Check that we can access or open it.  */
+	      if (NATNUMP (predicate))
+		fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
+		       && euidaccess (pfn, XFASTINT (predicate)) == 0
+		       && ! file_directory_p (pfn))
+		      ? 1 : -1);
+	      else
 		{
-		  /* Check that we can access or open it.  */
-		  if (NATNUMP (predicate))
-		    fd = (((XFASTINT (predicate) & ~INT_MAX) == 0
-			   && access (pfn, XFASTINT (predicate)) == 0)
-			  ? 1 : -1);
-		  else
-		    fd = emacs_open (pfn, O_RDONLY, 0);
-
-		  if (fd >= 0)
+		  struct stat st;
+		  fd = emacs_open (pfn, O_RDONLY, 0);
+		  if (0 <= fd
+		      && (fstat (fd, &st) != 0 || S_ISDIR (st.st_mode)))
 		    {
-		      /* We succeeded; return this descriptor and filename.  */
-		      if (storeptr)
-			*storeptr = string;
-		      UNGCPRO;
-		      return fd;
+		      emacs_close (fd);
+		      fd = -1;
 		    }
 		}
+
+	      if (fd >= 0)
+		{
+		  /* We succeeded; return this descriptor and filename.  */
+		  if (storeptr)
+		    *storeptr = string;
+		  UNGCPRO;
+		  return fd;
+		}
 	    }
 	}
       if (absolute)
@@ -4088,7 +4088,7 @@
       if (STRINGP (dirfile))
         {
           dirfile = Fdirectory_file_name (dirfile);
-          if (access (SSDATA (dirfile), 0) < 0)
+          if (euidaccess (SSDATA (dirfile), F_OK) != 0)
             dir_warning ("Warning: Lisp directory `%s' does not exist.\n",
                          XCAR (path_tail));
         }

=== modified file 'src/process.c'
--- src/process.c	2012-10-13 00:52:01 +0000
+++ src/process.c	2012-10-13 00:55:20 +0000
@@ -663,7 +663,7 @@
 #else
 	    sprintf (pty_name, "/dev/tty%c%x", c, i);
 #endif /* no PTY_TTY_NAME_SPRINTF */
-	    if (access (pty_name, 6) != 0)
+	    if (euidaccess (pty_name, R_OK | W_OK) != 0)
 	      {
 		emacs_close (fd);
 # ifndef __sgi

=== modified file 'src/xrdb.c'
--- src/xrdb.c	2012-09-15 07:06:56 +0000
+++ src/xrdb.c	2012-10-12 23:28:31 +0000
@@ -268,7 +268,7 @@
 {
   struct stat status;
 
-  return (access (filename, 4) == 0             /* exists and is readable */
+  return (euidaccess (filename, R_OK) == 0      /* exists and is readable */
 	  && stat (filename, &status) == 0	/* get the status */
 	  && (S_ISDIR (status.st_mode)) == 0);	/* not a directory */
 }


  reply	other threads:[~2012-10-14  6:16 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-10-13  1:58 bug#12632: file permissions checking mishandled when setuid Paul Eggert
2012-10-13  7:23 ` Eli Zaretskii
2012-10-13  8:36   ` Eli Zaretskii
2012-10-14  6:16     ` Paul Eggert [this message]
2012-10-14  6:56       ` Eli Zaretskii
2012-10-14 18:14         ` Paul Eggert
2012-10-14 18:39           ` Eli Zaretskii
2012-10-14 19:42             ` Paul Eggert
2012-10-14 20:10               ` Eli Zaretskii
2012-10-14 20:17               ` Eli Zaretskii
2012-10-14 20:40                 ` Paul Eggert
2012-10-14 20:53                   ` Eli Zaretskii
2012-10-15  6:17                     ` Paul Eggert
2012-10-15 17:31                       ` Eli Zaretskii
2012-10-15 21:38                         ` Paul Eggert
2012-10-16  3:46                           ` Eli Zaretskii
2012-10-16  6:00                             ` Paul Eggert
2012-10-16 16:36                               ` Eli Zaretskii
2012-10-19 17:01                                 ` Paul Eggert
2012-10-19 18:41                                   ` Eli Zaretskii
2012-10-19 18:54                                     ` Paul Eggert
2012-10-19 19:05                                       ` Glenn Morris
2012-10-19 19:36                                         ` Paul Eggert
2012-10-20  2:25                                           ` Richard Stallman
2012-10-20  4:36                                             ` Paul Eggert
2012-10-21  1:44                                           ` Glenn Morris
2012-10-21  2:52                                             ` Paul Eggert
2012-10-21  4:24                                               ` Glenn Morris
2012-10-22  6:03                                                 ` Paul Eggert
2012-10-22 17:19                                                   ` Eli Zaretskii
2012-10-22 20:33                                                     ` Paul Eggert
2012-10-22 21:04                                                       ` Eli Zaretskii
2012-10-22 21:30                                                         ` Paul Eggert
2012-10-23  0:40                                                           ` Stefan Monnier
2012-10-23  1:46                                                             ` Paul Eggert
2012-10-23  3:49                                                               ` Eli Zaretskii
2012-10-23  3:47                                                           ` Eli Zaretskii
2012-10-23  5:07                                                             ` Paul Eggert
2012-10-23 16:44                                                               ` Eli Zaretskii
2012-10-23 19:27                                                                 ` Paul Eggert
2012-10-23 19:50                                                                   ` Eli Zaretskii
2012-10-23 20:01                                                                     ` Paul Eggert
2012-10-23 23:15                                                                   ` Andy Moreton
2012-10-24  3:51                                                                     ` Eli Zaretskii
2012-10-19 19:10                                       ` Eli Zaretskii
2012-11-13  2:19 ` bug#12632: updated version of the patch Paul Eggert
2012-11-14  5:10   ` Paul Eggert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=507A58CC.10209@cs.ucla.edu \
    --to=eggert@cs.ucla.edu \
    --cc=12632@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).