1/* eaccess.c - eaccess replacement for the shell, plus other access functions. */ 2 3/* Copyright (C) 2006 Free Software Foundation, Inc. 4 5 This file is part of GNU Bash, the Bourne Again SHell. 6 7 Bash is free software; you can redistribute it and/or modify it under 8 the terms of the GNU General Public License as published by the Free 9 Software Foundation; either version 2, or (at your option) any later 10 version. 11 12 Bash is distributed in the hope that it will be useful, but WITHOUT ANY 13 WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 for more details. 16 17 You should have received a copy of the GNU General Public License along 18 with Bash; see the file COPYING. If not, write to the Free Software 19 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 20 21#if defined (HAVE_CONFIG_H) 22# include <config.h> 23#endif 24 25#include <stdio.h> 26 27#include "bashtypes.h" 28 29#if defined (HAVE_UNISTD_H) 30# include <unistd.h> 31#endif 32 33#include "bashansi.h" 34 35#include <errno.h> 36#if !defined (errno) 37extern int errno; 38#endif /* !errno */ 39 40#if !defined (_POSIX_VERSION) && defined (HAVE_SYS_FILE_H) 41# include <sys/file.h> 42#endif /* !_POSIX_VERSION */ 43#include "posixstat.h" 44#include "filecntl.h" 45 46#include "shell.h" 47 48#if !defined (R_OK) 49#define R_OK 4 50#define W_OK 2 51#define X_OK 1 52#define F_OK 0 53#endif /* R_OK */ 54 55static int path_is_devfd __P((const char *)); 56static int sh_stataccess __P((char *, int)); 57#if HAVE_DECL_SETREGID 58static int sh_euidaccess __P((char *, int)); 59#endif 60 61static int 62path_is_devfd (path) 63 const char *path; 64{ 65 if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) 66 return 1; 67 else if (STREQN (path, "/dev/std", 8)) 68 { 69 if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err")) 70 return 1; 71 else 72 return 0; 73 } 74 else 75 return 0; 76} 77 78/* A wrapper for stat () which disallows pathnames that are empty strings 79 and handles /dev/fd emulation on systems that don't have it. */ 80int 81sh_stat (path, finfo) 82 const char *path; 83 struct stat *finfo; 84{ 85 if (*path == '\0') 86 { 87 errno = ENOENT; 88 return (-1); 89 } 90 if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0) 91 { 92#if !defined (HAVE_DEV_FD) 93 intmax_t fd; 94 int r; 95 96 if (legal_number (path + 8, &fd) && fd == (int)fd) 97 { 98 r = fstat ((int)fd, finfo); 99 if (r == 0 || errno != EBADF) 100 return (r); 101 } 102 errno = ENOENT; 103 return (-1); 104#else 105 /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a 106 trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx. 107 On most systems, with the notable exception of linux, this is 108 effectively a no-op. */ 109 size_t size = strlen(DEV_FD_PREFIX)+strlen(path+8)+1; 110 char pbuf[size]; 111 snprintf (pbuf, sizeof(pbuf), "%s%s", DEV_FD_PREFIX, path + 8); 112 return (stat (pbuf, finfo)); 113#endif /* !HAVE_DEV_FD */ 114 } 115#if !defined (HAVE_DEV_STDIN) 116 else if (STREQN (path, "/dev/std", 8)) 117 { 118 if (STREQ (path+8, "in")) 119 return (fstat (0, finfo)); 120 else if (STREQ (path+8, "out")) 121 return (fstat (1, finfo)); 122 else if (STREQ (path+8, "err")) 123 return (fstat (2, finfo)); 124 else 125 return (stat (path, finfo)); 126 } 127#endif /* !HAVE_DEV_STDIN */ 128 return (stat (path, finfo)); 129} 130 131/* Do the same thing access(2) does, but use the effective uid and gid, 132 and don't make the mistake of telling root that any file is 133 executable. This version uses stat(2). */ 134static int 135sh_stataccess (path, mode) 136 char *path; 137 int mode; 138{ 139 struct stat st; 140 141 if (sh_stat (path, &st) < 0) 142 return (-1); 143 144 if (current_user.euid == 0) 145 { 146 /* Root can read or write any file. */ 147 if ((mode & X_OK) == 0) 148 return (0); 149 150 /* Root can execute any file that has any one of the execute 151 bits set. */ 152 if (st.st_mode & S_IXUGO) 153 return (0); 154 } 155 156 if (st.st_uid == current_user.euid) /* owner */ 157 mode <<= 6; 158 else if (group_member (st.st_gid)) 159 mode <<= 3; 160 161 if (st.st_mode & mode) 162 return (0); 163 164 errno = EACCES; 165 return (-1); 166} 167 168#if HAVE_DECL_SETREGID 169/* Version to call when uid != euid or gid != egid. We temporarily swap 170 the effective and real uid and gid as appropriate. */ 171static int 172sh_euidaccess (path, mode) 173 char *path; 174 int mode; 175{ 176 int r, e; 177 178 if (current_user.uid != current_user.euid) 179 setreuid (current_user.euid, current_user.uid); 180 if (current_user.gid != current_user.egid) 181 setregid (current_user.egid, current_user.gid); 182 183 r = access (path, mode); 184 e = errno; 185 186 if (current_user.uid != current_user.euid) 187 setreuid (current_user.uid, current_user.euid); 188 if (current_user.gid != current_user.egid) 189 setregid (current_user.gid, current_user.egid); 190 191 errno = e; 192 return r; 193} 194#endif 195 196int 197sh_eaccess (path, mode) 198 char *path; 199 int mode; 200{ 201 if (path_is_devfd (path)) 202 return (sh_stataccess (path, mode)); 203 204#if defined (HAVE_EACCESS) /* FreeBSD */ 205 return (eaccess (path, mode)); 206#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */ 207 return access (path, mode|EFF_ONLY_OK); 208#else 209 if (mode == F_OK) 210 return (sh_stataccess (path, mode)); 211 212# if HAVE_DECL_SETREGID 213 if (current_user.uid != current_user.euid || current_user.gid != current_user.egid) 214 return (sh_euidaccess (path, mode)); 215# endif 216 217 if (current_user.uid == current_user.euid && current_user.gid == current_user.egid) 218 return (access (path, mode)); 219 220 return (sh_stataccess (path, mode)); 221#endif 222} 223