| 
									
										
										
										
											2006-10-10 14:15:34 +00:00
										 |  |  | /* eaccess.c - eaccess replacement for the shell, plus other access functions. */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 19:11:26 -05:00
										 |  |  | /* Copyright (C) 2006-2010 Free Software Foundation, Inc.
 | 
					
						
							| 
									
										
										
										
											2006-10-10 14:15:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |    This file is part of GNU Bash, the Bourne Again SHell. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-12 13:36:28 +00:00
										 |  |  |    Bash 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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2006-10-10 14:15:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #if defined (HAVE_CONFIG_H)
 | 
					
						
							|  |  |  | #  include <config.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "bashtypes.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined (HAVE_UNISTD_H)
 | 
					
						
							|  |  |  | #  include <unistd.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "bashansi.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | #if !defined (errno)
 | 
					
						
							|  |  |  | extern int errno; | 
					
						
							|  |  |  | #endif /* !errno */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H)
 | 
					
						
							|  |  |  | #  include <sys/file.h>
 | 
					
						
							|  |  |  | #endif /* !_POSIX_VERSION */
 | 
					
						
							|  |  |  | #include "posixstat.h"
 | 
					
						
							|  |  |  | #include "filecntl.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "shell.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if !defined (R_OK)
 | 
					
						
							|  |  |  | #define R_OK 4
 | 
					
						
							|  |  |  | #define W_OK 2
 | 
					
						
							|  |  |  | #define X_OK 1
 | 
					
						
							|  |  |  | #define F_OK 0
 | 
					
						
							|  |  |  | #endif /* R_OK */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int path_is_devfd __P((const char *)); | 
					
						
							|  |  |  | static int sh_stataccess __P((char *, int)); | 
					
						
							|  |  |  | #if HAVE_DECL_SETREGID
 | 
					
						
							|  |  |  | static int sh_euidaccess __P((char *, int)); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | path_is_devfd (path) | 
					
						
							|  |  |  |      const char *path; | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  |   else if (STREQN (path, "/dev/std", 8)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err")) | 
					
						
							|  |  |  | 	return 1; | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* A wrapper for stat () which disallows pathnames that are empty strings
 | 
					
						
							|  |  |  |    and handles /dev/fd emulation on systems that don't have it. */ | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | sh_stat (path, finfo) | 
					
						
							|  |  |  |      const char *path; | 
					
						
							|  |  |  |      struct stat *finfo; | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (*path == '\0') | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       errno = ENOENT; | 
					
						
							|  |  |  |       return (-1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  | #if !defined (HAVE_DEV_FD)
 | 
					
						
							|  |  |  |       intmax_t fd; | 
					
						
							|  |  |  |       int r; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (legal_number (path + 8, &fd) && fd == (int)fd) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           r = fstat ((int)fd, finfo); | 
					
						
							|  |  |  |           if (r == 0 || errno != EBADF) | 
					
						
							|  |  |  |             return (r); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       errno = ENOENT; | 
					
						
							|  |  |  |       return (-1); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |   /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
 | 
					
						
							|  |  |  |      trailing slash.  Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx. | 
					
						
							|  |  |  |      On most systems, with the notable exception of linux, this is | 
					
						
							|  |  |  |      effectively a no-op. */ | 
					
						
							|  |  |  |       char pbuf[32]; | 
					
						
							|  |  |  |       strcpy (pbuf, DEV_FD_PREFIX); | 
					
						
							|  |  |  |       strcat (pbuf, path + 8); | 
					
						
							|  |  |  |       return (stat (pbuf, finfo)); | 
					
						
							|  |  |  | #endif /* !HAVE_DEV_FD */
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #if !defined (HAVE_DEV_STDIN)
 | 
					
						
							|  |  |  |   else if (STREQN (path, "/dev/std", 8)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (STREQ (path+8, "in")) | 
					
						
							|  |  |  | 	return (fstat (0, finfo)); | 
					
						
							|  |  |  |       else if (STREQ (path+8, "out")) | 
					
						
							|  |  |  | 	return (fstat (1, finfo)); | 
					
						
							|  |  |  |       else if (STREQ (path+8, "err")) | 
					
						
							|  |  |  | 	return (fstat (2, finfo)); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  | 	return (stat (path, finfo)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif /* !HAVE_DEV_STDIN */
 | 
					
						
							|  |  |  |   return (stat (path, finfo)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Do the same thing access(2) does, but use the effective uid and gid,
 | 
					
						
							|  |  |  |    and don't make the mistake of telling root that any file is | 
					
						
							|  |  |  |    executable.  This version uses stat(2). */ | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | sh_stataccess (path, mode) | 
					
						
							|  |  |  |      char *path; | 
					
						
							|  |  |  |      int mode; | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   struct stat st; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (sh_stat (path, &st) < 0) | 
					
						
							|  |  |  |     return (-1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (current_user.euid == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       /* Root can read or write any file. */ | 
					
						
							|  |  |  |       if ((mode & X_OK) == 0) | 
					
						
							|  |  |  | 	return (0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       /* Root can execute any file that has any one of the execute
 | 
					
						
							|  |  |  | 	 bits set. */ | 
					
						
							|  |  |  |       if (st.st_mode & S_IXUGO) | 
					
						
							|  |  |  | 	return (0); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (st.st_uid == current_user.euid)	/* owner */ | 
					
						
							|  |  |  |     mode <<= 6; | 
					
						
							|  |  |  |   else if (group_member (st.st_gid)) | 
					
						
							|  |  |  |     mode <<= 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (st.st_mode & mode) | 
					
						
							|  |  |  |     return (0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   errno = EACCES; | 
					
						
							|  |  |  |   return (-1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if HAVE_DECL_SETREGID
 | 
					
						
							|  |  |  | /* Version to call when uid != euid or gid != egid.  We temporarily swap
 | 
					
						
							|  |  |  |    the effective and real uid and gid as appropriate. */ | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | sh_euidaccess (path, mode) | 
					
						
							|  |  |  |      char *path; | 
					
						
							|  |  |  |      int mode; | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   int r, e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (current_user.uid != current_user.euid) | 
					
						
							|  |  |  |     setreuid (current_user.euid, current_user.uid); | 
					
						
							|  |  |  |   if (current_user.gid != current_user.egid) | 
					
						
							|  |  |  |     setregid (current_user.egid, current_user.gid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   r = access (path, mode); | 
					
						
							|  |  |  |   e = errno; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (current_user.uid != current_user.euid) | 
					
						
							|  |  |  |     setreuid (current_user.uid, current_user.euid); | 
					
						
							|  |  |  |   if (current_user.gid != current_user.egid) | 
					
						
							|  |  |  |     setregid (current_user.gid, current_user.egid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   errno = e; | 
					
						
							|  |  |  |   return r;   | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | sh_eaccess (path, mode) | 
					
						
							|  |  |  |      char *path; | 
					
						
							|  |  |  |      int mode; | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-22 19:11:26 -05:00
										 |  |  |   int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-10 14:15:34 +00:00
										 |  |  |   if (path_is_devfd (path)) | 
					
						
							|  |  |  |     return (sh_stataccess (path, mode)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 19:11:26 -05:00
										 |  |  | #if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
 | 
					
						
							|  |  |  |   return (faccessat (AT_FDCWD, path, mode, AT_EACCESS)); | 
					
						
							|  |  |  | #elif defined (HAVE_EACCESS)		/* FreeBSD */
 | 
					
						
							|  |  |  |   ret = eaccess (path, mode);	/* XXX -- not always correct for X_OK */ | 
					
						
							|  |  |  | #  if defined (__FreeBSD__)
 | 
					
						
							|  |  |  |   if (ret == 0 && current_user.euid == 0 && mode == X_OK) | 
					
						
							|  |  |  |     return (sh_stataccess (path, mode)); | 
					
						
							|  |  |  | #  endif
 | 
					
						
							|  |  |  |   return ret; | 
					
						
							| 
									
										
										
										
											2006-10-10 14:15:34 +00:00
										 |  |  | #elif defined (EFF_ONLY_OK)		/* SVR4(?), SVR4.2 */
 | 
					
						
							|  |  |  |   return access (path, mode|EFF_ONLY_OK); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |   if (mode == F_OK) | 
					
						
							|  |  |  |     return (sh_stataccess (path, mode)); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | #  if HAVE_DECL_SETREGID
 | 
					
						
							|  |  |  |   if (current_user.uid != current_user.euid || current_user.gid != current_user.egid) | 
					
						
							|  |  |  |     return (sh_euidaccess (path, mode)); | 
					
						
							|  |  |  | #  endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (current_user.uid == current_user.euid && current_user.gid == current_user.egid) | 
					
						
							| 
									
										
										
										
											2011-11-22 19:11:26 -05:00
										 |  |  |     { | 
					
						
							|  |  |  |       ret = access (path, mode); | 
					
						
							|  |  |  | #if defined (__FreeBSD__) || defined (SOLARIS)
 | 
					
						
							|  |  |  |       if (ret == 0 && current_user.euid == 0 && mode == X_OK) | 
					
						
							|  |  |  | 	return (sh_stataccess (path, mode)); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |       return ret; | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2006-10-10 14:15:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return (sh_stataccess (path, mode)); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } |