blob: ccb2f22fb035833abab06bf91cffaad36c37ecf1 [file] [log] [blame]
Jari Aalto06285672006-10-10 14:15:34 +00001/* eaccess.c - eaccess replacement for the shell, plus other access functions. */
2
Chet Ramey495aee42011-11-22 19:11:26 -05003/* Copyright (C) 2006-2010 Free Software Foundation, Inc.
Jari Aalto06285672006-10-10 14:15:34 +00004
5 This file is part of GNU Bash, the Bourne Again SHell.
6
Jari Aalto31859422009-01-12 13:36:28 +00007 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.
Jari Aalto06285672006-10-10 14:15:34 +000011
Jari Aalto31859422009-01-12 13:36:28 +000012 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.
Jari Aalto06285672006-10-10 14:15:34 +000016
Jari Aalto31859422009-01-12 13:36:28 +000017 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*/
Jari Aalto06285672006-10-10 14:15:34 +000020
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)
Ricardo Cerqueiraa02fbff2013-07-25 22:35:34 +010037#include <errno.h>
Jari Aalto06285672006-10-10 14:15:34 +000038#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{
Chet Ramey14459df2012-07-10 09:44:17 -040085 static char *pbuf = 0;
86
Jari Aalto06285672006-10-10 14:15:34 +000087 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 !defined (HAVE_DEV_FD)
95 intmax_t fd;
96 int r;
97
98 if (legal_number (path + 8, &fd) && fd == (int)fd)
99 {
100 r = fstat ((int)fd, finfo);
101 if (r == 0 || errno != EBADF)
102 return (r);
103 }
104 errno = ENOENT;
105 return (-1);
106#else
107 /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
108 trailing slash. Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
109 On most systems, with the notable exception of linux, this is
110 effectively a no-op. */
Chet Ramey14459df2012-07-10 09:44:17 -0400111 pbuf = xrealloc (pbuf, sizeof (DEV_FD_PREFIX) + strlen (path + 8));
Jari Aalto06285672006-10-10 14:15:34 +0000112 strcpy (pbuf, DEV_FD_PREFIX);
113 strcat (pbuf, path + 8);
114 return (stat (pbuf, finfo));
115#endif /* !HAVE_DEV_FD */
116 }
117#if !defined (HAVE_DEV_STDIN)
118 else if (STREQN (path, "/dev/std", 8))
119 {
120 if (STREQ (path+8, "in"))
121 return (fstat (0, finfo));
122 else if (STREQ (path+8, "out"))
123 return (fstat (1, finfo));
124 else if (STREQ (path+8, "err"))
125 return (fstat (2, finfo));
126 else
127 return (stat (path, finfo));
128 }
129#endif /* !HAVE_DEV_STDIN */
130 return (stat (path, finfo));
131}
132
133/* Do the same thing access(2) does, but use the effective uid and gid,
134 and don't make the mistake of telling root that any file is
135 executable. This version uses stat(2). */
136static int
137sh_stataccess (path, mode)
138 char *path;
139 int mode;
140{
141 struct stat st;
142
143 if (sh_stat (path, &st) < 0)
144 return (-1);
145
146 if (current_user.euid == 0)
147 {
148 /* Root can read or write any file. */
149 if ((mode & X_OK) == 0)
150 return (0);
151
152 /* Root can execute any file that has any one of the execute
153 bits set. */
154 if (st.st_mode & S_IXUGO)
155 return (0);
156 }
157
158 if (st.st_uid == current_user.euid) /* owner */
159 mode <<= 6;
160 else if (group_member (st.st_gid))
161 mode <<= 3;
162
163 if (st.st_mode & mode)
164 return (0);
165
166 errno = EACCES;
167 return (-1);
168}
169
170#if HAVE_DECL_SETREGID
171/* Version to call when uid != euid or gid != egid. We temporarily swap
172 the effective and real uid and gid as appropriate. */
173static int
174sh_euidaccess (path, mode)
175 char *path;
176 int mode;
177{
178 int r, e;
179
180 if (current_user.uid != current_user.euid)
181 setreuid (current_user.euid, current_user.uid);
182 if (current_user.gid != current_user.egid)
183 setregid (current_user.egid, current_user.gid);
184
185 r = access (path, mode);
186 e = errno;
187
188 if (current_user.uid != current_user.euid)
189 setreuid (current_user.uid, current_user.euid);
190 if (current_user.gid != current_user.egid)
191 setregid (current_user.gid, current_user.egid);
192
193 errno = e;
194 return r;
195}
196#endif
197
198int
199sh_eaccess (path, mode)
200 char *path;
201 int mode;
202{
Chet Ramey495aee42011-11-22 19:11:26 -0500203 int ret;
204
Jari Aalto06285672006-10-10 14:15:34 +0000205 if (path_is_devfd (path))
206 return (sh_stataccess (path, mode));
207
Chet Rameyac50fba2014-02-26 09:36:43 -0500208#if (defined (HAVE_FACCESSAT) && defined (AT_EACCESS)) || defined (HAVE_EACCESS)
209# if defined (HAVE_FACCESSAT) && defined (AT_EACCESS)
210 ret = faccessat (AT_FDCWD, path, mode, AT_EACCESS);
211# else /* HAVE_EACCESS */ /* FreeBSD */
Chet Ramey495aee42011-11-22 19:11:26 -0500212 ret = eaccess (path, mode); /* XXX -- not always correct for X_OK */
Chet Rameyac50fba2014-02-26 09:36:43 -0500213# endif /* HAVE_EACCESS */
214# if defined (__FreeBSD__) || defined (SOLARIS)
Chet Ramey495aee42011-11-22 19:11:26 -0500215 if (ret == 0 && current_user.euid == 0 && mode == X_OK)
216 return (sh_stataccess (path, mode));
Chet Rameyac50fba2014-02-26 09:36:43 -0500217# endif /* __FreeBSD__ || SOLARIS */
Chet Ramey495aee42011-11-22 19:11:26 -0500218 return ret;
Jari Aalto06285672006-10-10 14:15:34 +0000219#elif defined (EFF_ONLY_OK) /* SVR4(?), SVR4.2 */
220 return access (path, mode|EFF_ONLY_OK);
221#else
222 if (mode == F_OK)
223 return (sh_stataccess (path, mode));
224
225# if HAVE_DECL_SETREGID
226 if (current_user.uid != current_user.euid || current_user.gid != current_user.egid)
227 return (sh_euidaccess (path, mode));
228# endif
229
230 if (current_user.uid == current_user.euid && current_user.gid == current_user.egid)
Chet Ramey495aee42011-11-22 19:11:26 -0500231 {
232 ret = access (path, mode);
233#if defined (__FreeBSD__) || defined (SOLARIS)
234 if (ret == 0 && current_user.euid == 0 && mode == X_OK)
235 return (sh_stataccess (path, mode));
236#endif
237 return ret;
Chet Ramey495aee42011-11-22 19:11:26 -0500238 }
Jari Aalto06285672006-10-10 14:15:34 +0000239
240 return (sh_stataccess (path, mode));
241#endif
242}