Line data Source code
1 : /* eaccess.c - eaccess replacement for the shell, plus other access functions. */
2 :
3 : /* Copyright (C) 2006-2010 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 __P((const char *));
56 : static int sh_stataccess __P((const char *, int));
57 : #if HAVE_DECL_SETREGID && \
58 : !(defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) && \
59 : !defined (HAVE_EACCESS) && !defined (EFF_ONLY_OK)
60 : static int sh_euidaccess __P((const char *, int));
61 : #endif
62 :
63 : static int
64 9171656 : path_is_devfd (path)
65 : const char *path;
66 : {
67 9171656 : if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
68 : return 1;
69 9171656 : else if (STREQN (path, "/dev/std", 8))
70 : {
71 0 : if (STREQ (path+8, "in") || STREQ (path+8, "out") || STREQ (path+8, "err"))
72 : return 1;
73 : else
74 0 : return 0;
75 : }
76 : else
77 : return 0;
78 : }
79 :
80 : /* A wrapper for stat () which disallows pathnames that are empty strings
81 : and handles /dev/fd emulation on systems that don't have it. */
82 : int
83 5531740 : sh_stat (path, finfo)
84 : const char *path;
85 : struct stat *finfo;
86 : {
87 5531740 : static char *pbuf = 0;
88 :
89 5531740 : if (*path == '\0')
90 : {
91 14 : errno = ENOENT;
92 14 : return (-1);
93 : }
94 5531726 : if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
95 : {
96 : #if !defined (HAVE_DEV_FD)
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 0 : pbuf = xrealloc (pbuf, sizeof (DEV_FD_PREFIX) + strlen (path + 8));
114 0 : strcpy (pbuf, DEV_FD_PREFIX);
115 0 : strcat (pbuf, path + 8);
116 0 : 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 11063452 : 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
139 0 : sh_stataccess (path, mode)
140 : const char *path;
141 : int mode;
142 : {
143 0 : struct stat st;
144 :
145 0 : if (sh_stat (path, &st) < 0)
146 : return (-1);
147 :
148 0 : if (current_user.euid == 0)
149 : {
150 : /* Root can read or write any file. */
151 0 : 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 0 : if (st.st_mode & S_IXUGO)
157 : return (0);
158 : }
159 :
160 0 : if (st.st_uid == current_user.euid) /* owner */
161 0 : mode <<= 6;
162 0 : else if (group_member (st.st_gid))
163 0 : mode <<= 3;
164 :
165 0 : if (st.st_mode & mode)
166 : return (0);
167 :
168 0 : errno = EACCES;
169 0 : return (-1);
170 : }
171 :
172 : #if HAVE_DECL_SETREGID && \
173 : !(defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) && \
174 : !defined (HAVE_EACCESS) && !defined (EFF_ONLY_OK)
175 : /* Version to call when uid != euid or gid != egid. We temporarily swap
176 : the effective and real uid and gid as appropriate. */
177 : static int
178 : sh_euidaccess (path, mode)
179 : const char *path;
180 : int mode;
181 : {
182 : int r, e;
183 :
184 : if (current_user.uid != current_user.euid)
185 : setreuid (current_user.euid, current_user.uid);
186 : if (current_user.gid != current_user.egid)
187 : setregid (current_user.egid, current_user.gid);
188 :
189 : r = access (path, mode);
190 : e = errno;
191 :
192 : if (current_user.uid != current_user.euid)
193 : setreuid (current_user.uid, current_user.euid);
194 : if (current_user.gid != current_user.egid)
195 : setregid (current_user.gid, current_user.egid);
196 :
197 : errno = e;
198 : return r;
199 : }
200 : #endif
201 :
202 : int
203 9171656 : sh_eaccess (path, mode)
204 : const char *path;
205 : int mode;
206 : {
207 9171656 : int ret;
208 :
209 9171656 : if (path_is_devfd (path))
210 0 : return (sh_stataccess (path, mode));
211 :
212 : #if (defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) || defined (HAVE_EACCESS)
213 : # if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
214 9171656 : ret = faccessat (AT_FDCWD, path, mode, AT_EACCESS);
215 : # else /* HAVE_EACCESS */ /* FreeBSD */
216 : ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */
217 : # endif /* HAVE_EACCESS */
218 : # if defined (__FreeBSD__) || defined (SOLARIS)
219 : if (ret == 0 && current_user.euid == 0 && mode == X_OK)
220 : return (sh_stataccess (path, mode));
221 : # endif /* __FreeBSD__ || SOLARIS */
222 9171656 : return ret;
223 : #elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
224 : return access (path, mode|EFF_ONLY_OK);
225 : #else
226 : if (mode == F_OK)
227 : return (sh_stataccess (path, mode));
228 :
229 : # if HAVE_DECL_SETREGID
230 : if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
231 : return (sh_euidaccess (path, mode));
232 : # endif
233 :
234 : if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
235 : {
236 : ret = access (path, mode);
237 : #if defined (__FreeBSD__) || defined (SOLARIS)
238 : if (ret == 0 && current_user.euid == 0 && mode == X_OK)
239 : return (sh_stataccess (path, mode));
240 : #endif
241 : return ret;
242 : }
243 :
244 : return (sh_stataccess (path, mode));
245 : #endif
246 : }
|