]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/sh/eaccess.c
d9bca8c40b448596af15b9180f655f54784637a5
[thirdparty/bash.git] / lib / sh / eaccess.c
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((char *, int));
57 #if HAVE_DECL_SETREGID
58 static int sh_euidaccess __P((char *, int));
59 #endif
60
61 static int
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
81 sh_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 char pbuf[32];
110 strcpy (pbuf, DEV_FD_PREFIX);
111 strcat (pbuf, 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). */
134 static int
135 sh_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. */
171 static int
172 sh_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
196 int
197 sh_eaccess (path, mode)
198 char *path;
199 int mode;
200 {
201 int ret;
202
203 if (path_is_devfd (path))
204 return (sh_stataccess (path, mode));
205
206 #if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
207 return (faccessat (AT_FDCWD, path, mode, AT_EACCESS));
208 #elif defined (HAVE_EACCESS) /* FreeBSD */
209 ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */
210 # if defined (__FreeBSD__)
211 if (ret == 0 && current_user.euid == 0 && mode == X_OK)
212 return (sh_stataccess (path, mode));
213 # endif
214 return ret;
215 #elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
216 return access (path, mode|EFF_ONLY_OK);
217 #else
218 if (mode == F_OK)
219 return (sh_stataccess (path, mode));
220
221 # if HAVE_DECL_SETREGID
222 if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
223 return (sh_euidaccess (path, mode));
224 # endif
225
226 if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
227 {
228 ret = access (path, mode);
229 #if defined (__FreeBSD__) || defined (SOLARIS)
230 if (ret == 0 && current_user.euid == 0 && mode == X_OK)
231 return (sh_stataccess (path, mode));
232 #endif
233 return ret;
234
235 }
236
237 return (sh_stataccess (path, mode));
238 #endif
239 }