1 /* eaccess.c - eaccess replacement for the shell, plus other access functions. */
2 
3 /* Copyright (C) 2006-2020 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
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    Bash is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
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)
37 extern 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 
55 static int path_is_devfd PARAMS((const char *));
56 static int sh_stataccess PARAMS((const char *, int));
57 #if HAVE_DECL_SETREGID
58 static int sh_euidaccess PARAMS((const char *, int));
59 #endif
60 
61 static int
path_is_devfd(path)62 path_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. */
80 int
sh_stat(path,finfo)81 sh_stat (path, finfo)
82      const char *path;
83      struct stat *finfo;
84 {
85   static char *pbuf = 0;
86 
87   if (*path == '\0')
88     {
89       errno = ENOENT;
90       return (-1);
91     }
92   if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
93     {
94       /* If stating /dev/fd/n doesn't produce the same results as fstat of
95 	 FD N, then define DEV_FD_STAT_BROKEN */
96 #if !defined (HAVE_DEV_FD) || defined (DEV_FD_STAT_BROKEN)
97       intmax_t fd;
98       int r;
99 
100       if (legal_number (path + 8, &fd) && fd == (int)fd)
101         {
102           r = fstat ((int)fd, finfo);
103           if (r == 0 || errno != EBADF)
104             return (r);
105         }
106       errno = ENOENT;
107       return (-1);
108 #else
109   /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
110      trailing slash.  Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
111      On most systems, with the notable exception of linux, this is
112      effectively a no-op. */
113       pbuf = xrealloc (pbuf, sizeof (DEV_FD_PREFIX) + strlen (path + 8));
114       strcpy (pbuf, DEV_FD_PREFIX);
115       strcat (pbuf, path + 8);
116       return (stat (pbuf, finfo));
117 #endif /* !HAVE_DEV_FD */
118     }
119 #if !defined (HAVE_DEV_STDIN)
120   else if (STREQN (path, "/dev/std", 8))
121     {
122       if (STREQ (path+8, "in"))
123 	return (fstat (0, finfo));
124       else if (STREQ (path+8, "out"))
125 	return (fstat (1, finfo));
126       else if (STREQ (path+8, "err"))
127 	return (fstat (2, finfo));
128       else
129 	return (stat (path, finfo));
130     }
131 #endif /* !HAVE_DEV_STDIN */
132   return (stat (path, finfo));
133 }
134 
135 /* Do the same thing access(2) does, but use the effective uid and gid,
136    and don't make the mistake of telling root that any file is
137    executable.  This version uses stat(2). */
138 static int
sh_stataccess(path,mode)139 sh_stataccess (path, mode)
140      const char *path;
141      int mode;
142 {
143   struct stat st;
144 
145   if (sh_stat (path, &st) < 0)
146     return (-1);
147 
148   if (current_user.euid == 0)
149     {
150       /* Root can read or write any file. */
151       if ((mode & X_OK) == 0)
152 	return (0);
153 
154       /* Root can execute any file that has any one of the execute
155 	 bits set. */
156       if (st.st_mode & S_IXUGO)
157 	return (0);
158     }
159 
160   if (st.st_uid == current_user.euid)	/* owner */
161     mode <<= 6;
162   else if (group_member (st.st_gid))
163     mode <<= 3;
164 
165   if (st.st_mode & mode)
166     return (0);
167 
168   errno = EACCES;
169   return (-1);
170 }
171 
172 #if HAVE_DECL_SETREGID
173 /* Version to call when uid != euid or gid != egid.  We temporarily swap
174    the effective and real uid and gid as appropriate. */
175 static int
sh_euidaccess(path,mode)176 sh_euidaccess (path, mode)
177      const char *path;
178      int mode;
179 {
180   int r, e;
181 
182   if (current_user.uid != current_user.euid)
183     setreuid (current_user.euid, current_user.uid);
184   if (current_user.gid != current_user.egid)
185     setregid (current_user.egid, current_user.gid);
186 
187   r = access (path, mode);
188   e = errno;
189 
190   if (current_user.uid != current_user.euid)
191     setreuid (current_user.uid, current_user.euid);
192   if (current_user.gid != current_user.egid)
193     setregid (current_user.gid, current_user.egid);
194 
195   errno = e;
196   return r;
197 }
198 #endif
199 
200 int
sh_eaccess(path,mode)201 sh_eaccess (path, mode)
202      const char *path;
203      int mode;
204 {
205   int ret;
206 
207   if (path_is_devfd (path))
208     return (sh_stataccess (path, mode));
209 
210 #if (defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) || defined (HAVE_EACCESS)
211 #  if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
212   ret = faccessat (AT_FDCWD, path, mode, AT_EACCESS);
213 #  else		/* HAVE_EACCESS */	/* FreeBSD */
214   ret = eaccess (path, mode);	/* XXX -- not always correct for X_OK */
215 #  endif	/* HAVE_EACCESS */
216 #  if defined (__FreeBSD__) || defined (SOLARIS) || defined (_AIX)
217   if (ret == 0 && current_user.euid == 0 && mode == X_OK)
218     return (sh_stataccess (path, mode));
219 #  endif	/* __FreeBSD__ || SOLARIS || _AIX */
220   return ret;
221 #elif defined (EFF_ONLY_OK)		/* SVR4(?), SVR4.2 */
222   return access (path, mode|EFF_ONLY_OK);
223 #else
224   if (mode == F_OK)
225     return (sh_stataccess (path, mode));
226 
227 #  if HAVE_DECL_SETREGID
228   if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
229     return (sh_euidaccess (path, mode));
230 #  endif
231 
232   if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
233     {
234       ret = access (path, mode);
235 #if defined (__FreeBSD__) || defined (SOLARIS)
236       if (ret == 0 && current_user.euid == 0 && mode == X_OK)
237 	return (sh_stataccess (path, mode));
238 #endif
239       return ret;
240     }
241 
242   return (sh_stataccess (path, mode));
243 #endif
244 }
245