xref: /PHP-5.3/main/safe_mode.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                        |
16    +----------------------------------------------------------------------+
17  */
18 
19 /* $Id$ */
20 
21 #include "php.h"
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #if HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #include <sys/stat.h>
30 #include "ext/standard/pageinfo.h"
31 #include "safe_mode.h"
32 #include "SAPI.h"
33 #include "php_globals.h"
34 
35 /*
36  * php_checkuid
37  *
38  * This function has six modes:
39  *
40  * 0 - return invalid (0) if file does not exist
41  * 1 - return valid (1)  if file does not exist
42  * 2 - if file does not exist, check directory
43  * 3 - only check directory (needed for mkdir)
44  * 4 - check mode and param
45  * 5 - only check file
46  */
47 
php_checkuid_ex(const char * filename,const char * fopen_mode,int mode,int flags)48 PHPAPI int php_checkuid_ex(const char *filename, const char *fopen_mode, int mode, int flags)
49 {
50 	struct stat sb;
51 	int ret, nofile=0;
52 	long uid=0L, gid=0L, duid=0L, dgid=0L;
53 	char path[MAXPATHLEN];
54 	char *s, filenamecopy[MAXPATHLEN];
55 	TSRMLS_FETCH();
56 
57 	path[0] = '\0';
58 
59 	if (!filename) {
60 		return 0; /* path must be provided */
61 	}
62 
63 	if (strlcpy(filenamecopy, filename, MAXPATHLEN)>=MAXPATHLEN) {
64 		return 0;
65 	}
66 	filename=(char *)&filenamecopy;
67 
68 	if (fopen_mode) {
69 		if (fopen_mode[0] == 'r') {
70 			mode = CHECKUID_DISALLOW_FILE_NOT_EXISTS;
71 		} else {
72 			mode = CHECKUID_CHECK_FILE_AND_DIR;
73 		}
74 	}
75 
76 	/* First we see if the file is owned by the same user...
77 	 * If that fails, passthrough and check directory...
78 	 */
79 	if (mode != CHECKUID_ALLOW_ONLY_DIR) {
80 #if HAVE_BROKEN_GETCWD
81 		char ftest[MAXPATHLEN];
82 
83 		strcpy(ftest, filename);
84 		if (VCWD_GETCWD(ftest, sizeof(ftest)) == NULL) {
85 			strcpy(path, filename);
86 		} else
87 #endif
88 		expand_filepath(filename, path TSRMLS_CC);
89 
90 		ret = VCWD_STAT(path, &sb);
91 		if (ret < 0) {
92 			if (mode == CHECKUID_DISALLOW_FILE_NOT_EXISTS) {
93 				if ((flags & CHECKUID_NO_ERRORS) == 0) {
94 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename);
95 				}
96 				return 0;
97 			} else if (mode == CHECKUID_ALLOW_FILE_NOT_EXISTS) {
98 				if ((flags & CHECKUID_NO_ERRORS) == 0) {
99 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename);
100 				}
101 				return 1;
102 			}
103 			nofile = 1;
104 		} else {
105 			uid = sb.st_uid;
106 			gid = sb.st_gid;
107 			if (uid == php_getuid()) {
108 				return 1;
109  			} else if (PG(safe_mode_gid) && gid == php_getgid()) {
110  				return 1;
111 			}
112 		}
113 
114 		/* Trim off filename */
115 		if ((s = strrchr(path, DEFAULT_SLASH))) {
116 			if (*(s + 1) == '\0' && s != path) { /* make sure that the / is not the last character */
117 				*s = '\0';
118 				s = strrchr(path, DEFAULT_SLASH);
119 			}
120 			if (s) {
121 				if (s == path) {
122 					path[1] = '\0';
123 				} else {
124 					*s = '\0';
125 				}
126 			}
127 		}
128 	} else { /* CHECKUID_ALLOW_ONLY_DIR */
129 		s = strrchr(filename, DEFAULT_SLASH);
130 
131 		if (s == filename) {
132 			/* root dir */
133 			path[0] = DEFAULT_SLASH;
134 			path[1] = '\0';
135 		} else if (s && *(s + 1) != '\0') { /* make sure that the / is not the last character */
136 			*s = '\0';
137 			VCWD_REALPATH(filename, path);
138 			*s = DEFAULT_SLASH;
139 		} else {
140 			/* Under Solaris, getcwd() can fail if there are no
141 			 * read permissions on a component of the path, even
142 			 * though it has the required x permissions */
143 			path[0] = '.';
144 			path[1] = '\0';
145 			VCWD_GETCWD(path, sizeof(path));
146  		}
147 	} /* end CHECKUID_ALLOW_ONLY_DIR */
148 
149 	if (mode != CHECKUID_ALLOW_ONLY_FILE) {
150 		/* check directory */
151 		ret = VCWD_STAT(path, &sb);
152 		if (ret < 0) {
153 			if ((flags & CHECKUID_NO_ERRORS) == 0) {
154 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to access %s", filename);
155 			}
156 			return 0;
157 		}
158 		duid = sb.st_uid;
159 		dgid = sb.st_gid;
160 		if (duid == php_getuid()) {
161 			return 1;
162  		} else if (PG(safe_mode_gid) && dgid == php_getgid()) {
163  			return 1;
164 		} else {
165 			if (SG(rfc1867_uploaded_files)) {
166 				if (zend_hash_exists(SG(rfc1867_uploaded_files), (char *) filename, strlen(filename)+1)) {
167 					return 1;
168 				}
169 			}
170 		}
171 	}
172 
173 	if (mode == CHECKUID_ALLOW_ONLY_DIR) {
174 		uid = duid;
175 		gid = dgid;
176 		if (s) {
177 			*s = 0;
178 		}
179 	}
180 
181 	if (nofile) {
182 		uid = duid;
183 		gid = dgid;
184 		filename = path;
185 	}
186 
187 	if ((flags & CHECKUID_NO_ERRORS) == 0) {
188 		if (PG(safe_mode_gid)) {
189 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SAFE MODE Restriction in effect.  The script whose uid/gid is %ld/%ld is not allowed to access %s owned by uid/gid %ld/%ld", php_getuid(), php_getgid(), filename, uid, gid);
190 		} else {
191 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SAFE MODE Restriction in effect.  The script whose uid is %ld is not allowed to access %s owned by uid %ld", php_getuid(), filename, uid);
192 		}
193 	}
194 
195 	return 0;
196 }
197 
php_checkuid(const char * filename,const char * fopen_mode,int mode)198 PHPAPI int php_checkuid(const char *filename, const char *fopen_mode, int mode)
199 {
200 #ifdef NETWARE
201 /* NetWare don't have uid*/
202 	return 1;
203 #else
204 	return php_checkuid_ex(filename, fopen_mode, mode, 0);
205 #endif
206 }
207 
php_get_current_user(void)208 PHPAPI char *php_get_current_user(void)
209 {
210 	struct stat *pstat;
211 	TSRMLS_FETCH();
212 
213 	if (SG(request_info).current_user) {
214 		return SG(request_info).current_user;
215 	}
216 
217 	/* FIXME: I need to have this somehow handled if
218 	USE_SAPI is defined, because cgi will also be
219 	interfaced in USE_SAPI */
220 
221 	pstat = sapi_get_stat(TSRMLS_C);
222 
223 	if (!pstat) {
224 		return "";
225 	} else {
226 #ifdef PHP_WIN32
227 		char name[256];
228 		DWORD len = sizeof(name)-1;
229 
230 		if (!GetUserName(name, &len)) {
231 			return "";
232 		}
233 		name[len] = '\0';
234 		SG(request_info).current_user_length = len;
235 		SG(request_info).current_user = estrndup(name, len);
236 		return SG(request_info).current_user;
237 #else
238 		struct passwd *pwd;
239 #if defined(ZTS) && defined(HAVE_GETPWUID_R) && defined(_SC_GETPW_R_SIZE_MAX)
240 		struct passwd _pw;
241 		struct passwd *retpwptr = NULL;
242 		int pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
243 		char *pwbuf;
244 
245 		if (pwbuflen < 1) {
246 			return "";
247 		}
248 		pwbuf = emalloc(pwbuflen);
249 		if (getpwuid_r(pstat->st_uid, &_pw, pwbuf, pwbuflen, &retpwptr) != 0) {
250 			efree(pwbuf);
251 			return "";
252 		}
253 		pwd = &_pw;
254 #else
255 		if ((pwd=getpwuid(pstat->st_uid))==NULL) {
256 			return "";
257 		}
258 #endif
259 		SG(request_info).current_user_length = strlen(pwd->pw_name);
260 		SG(request_info).current_user = estrndup(pwd->pw_name, SG(request_info).current_user_length);
261 #if defined(ZTS) && defined(HAVE_GETPWUID_R) && defined(_SC_GETPW_R_SIZE_MAX)
262 		efree(pwbuf);
263 #endif
264 		return SG(request_info).current_user;
265 #endif
266 	}
267 }
268 
269 /*
270  * Local variables:
271  * tab-width: 4
272  * c-basic-offset: 4
273  * End:
274  * vim600: sw=4 ts=4 fdm=marker
275  * vim<600: sw=4 ts=4
276  */
277