1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2007 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 : | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
16 : | Jim Winstead <jimw@php.net> |
17 : +----------------------------------------------------------------------+
18 : */
19 :
20 : /* $Id: fopen_wrappers.c,v 1.175.2.3.2.9 2007/02/24 02:17:28 helly Exp $ */
21 :
22 : /* {{{ includes
23 : */
24 : #include "php.h"
25 : #include "php_globals.h"
26 : #include "SAPI.h"
27 :
28 : #include <stdio.h>
29 : #include <stdlib.h>
30 : #include <errno.h>
31 : #include <sys/types.h>
32 : #include <sys/stat.h>
33 : #include <fcntl.h>
34 :
35 : #ifdef PHP_WIN32
36 : #define O_RDONLY _O_RDONLY
37 : #include "win32/param.h"
38 : #else
39 : #include <sys/param.h>
40 : #endif
41 :
42 : #include "safe_mode.h"
43 : #include "ext/standard/head.h"
44 : #include "ext/standard/php_standard.h"
45 : #include "zend_compile.h"
46 : #include "php_network.h"
47 :
48 : #if HAVE_PWD_H
49 : #include <pwd.h>
50 : #endif
51 :
52 : #include <sys/types.h>
53 : #if HAVE_SYS_SOCKET_H
54 : #include <sys/socket.h>
55 : #endif
56 :
57 : #ifndef S_ISREG
58 : #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
59 : #endif
60 :
61 : #ifdef PHP_WIN32
62 : #include <winsock2.h>
63 : #elif defined(NETWARE) && defined(USE_WINSOCK)
64 : #include <novsock2.h>
65 : #else
66 : #include <netinet/in.h>
67 : #include <netdb.h>
68 : #if HAVE_ARPA_INET_H
69 : #include <arpa/inet.h>
70 : #endif
71 : #endif
72 :
73 : #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
74 : #undef AF_UNIX
75 : #endif
76 :
77 : #if defined(AF_UNIX)
78 : #include <sys/un.h>
79 : #endif
80 : /* }}} */
81 :
82 : /* {{{ php_check_specific_open_basedir
83 : When open_basedir is not NULL, check if the given filename is located in
84 : open_basedir. Returns -1 if error or not in the open_basedir, else 0
85 :
86 : When open_basedir is NULL, always return 0
87 : */
88 : PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path TSRMLS_DC)
89 0 : {
90 : char resolved_name[MAXPATHLEN];
91 : char resolved_basedir[MAXPATHLEN];
92 : char local_open_basedir[MAXPATHLEN];
93 : int resolved_basedir_len;
94 : int resolved_name_len;
95 :
96 : /* Special case basedir==".": Use script-directory */
97 0 : if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) {
98 : /* Else use the unmodified path */
99 0 : strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir));
100 : }
101 :
102 : /* Resolve the real path into resolved_name */
103 0 : if ((expand_filepath(path, resolved_name TSRMLS_CC) != NULL) && (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL)) {
104 : /* Handler for basedirs that end with a / */
105 0 : resolved_basedir_len = strlen(resolved_basedir);
106 0 : if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) {
107 0 : if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
108 0 : resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR;
109 0 : resolved_basedir[++resolved_basedir_len] = '\0';
110 : }
111 : }
112 :
113 0 : if (path[strlen(path)-1] == PHP_DIR_SEPARATOR) {
114 0 : resolved_name_len = strlen(resolved_name);
115 0 : if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) {
116 0 : resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR;
117 0 : resolved_name[++resolved_name_len] = '\0';
118 : }
119 : }
120 :
121 : /* Check the path */
122 : #if defined(PHP_WIN32) || defined(NETWARE)
123 : if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
124 : #else
125 0 : if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
126 : #endif
127 : /* File is in the right directory */
128 0 : return 0;
129 : } else {
130 0 : return -1;
131 : }
132 : } else {
133 : /* Unable to resolve the real path, return -1 */
134 0 : return -1;
135 : }
136 : }
137 : /* }}} */
138 :
139 : PHPAPI int php_check_open_basedir(const char *path TSRMLS_DC)
140 4324 : {
141 4324 : return php_check_open_basedir_ex(path, 1 TSRMLS_CC);
142 : }
143 :
144 : /* {{{ php_check_open_basedir
145 : */
146 : PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC)
147 5214 : {
148 : /* Only check when open_basedir is available */
149 5214 : if (PG(open_basedir) && *PG(open_basedir)) {
150 : char *pathbuf;
151 : char *ptr;
152 : char *end;
153 :
154 0 : pathbuf = estrdup(PG(open_basedir));
155 :
156 0 : ptr = pathbuf;
157 :
158 0 : while (ptr && *ptr) {
159 0 : end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
160 0 : if (end != NULL) {
161 0 : *end = '\0';
162 0 : end++;
163 : }
164 :
165 0 : if (php_check_specific_open_basedir(ptr, path TSRMLS_CC) == 0) {
166 0 : efree(pathbuf);
167 0 : return 0;
168 : }
169 :
170 0 : ptr = end;
171 : }
172 0 : if (warn) {
173 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
174 : "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
175 : }
176 0 : efree(pathbuf);
177 0 : errno = EPERM; /* we deny permission to open it */
178 0 : return -1;
179 : }
180 :
181 : /* Nothing to check... */
182 5214 : return 0;
183 : }
184 : /* }}} */
185 :
186 : /* {{{ php_check_safe_mode_include_dir
187 : */
188 : PHPAPI int php_check_safe_mode_include_dir(const char *path TSRMLS_DC)
189 0 : {
190 0 : if (PG(safe_mode)) {
191 0 : if (PG(safe_mode_include_dir) && *PG(safe_mode_include_dir)) {
192 : char *pathbuf;
193 : char *ptr;
194 : char *end;
195 : char resolved_name[MAXPATHLEN];
196 :
197 : /* Resolve the real path into resolved_name */
198 0 : if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL)
199 0 : return -1;
200 :
201 0 : pathbuf = estrdup(PG(safe_mode_include_dir));
202 :
203 0 : ptr = pathbuf;
204 :
205 0 : while (ptr && *ptr) {
206 0 : end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
207 0 : if (end != NULL) {
208 0 : *end = '\0';
209 0 : end++;
210 : }
211 :
212 : /* Check the path */
213 : #ifdef PHP_WIN32
214 : if (strncasecmp(ptr, resolved_name, strlen(ptr)) == 0)
215 : #else
216 0 : if (strncmp(ptr, resolved_name, strlen(ptr)) == 0)
217 : #endif
218 : {
219 : /* File is in the right directory */
220 0 : efree(pathbuf);
221 0 : return 0;
222 : }
223 :
224 0 : ptr = end;
225 : }
226 0 : efree(pathbuf);
227 : }
228 0 : return -1;
229 : }
230 :
231 : /* Nothing to check... */
232 0 : return 0;
233 : }
234 : /* }}} */
235 :
236 : /* {{{ php_fopen_and_set_opened_path
237 : */
238 : static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, char **opened_path TSRMLS_DC)
239 1318 : {
240 : FILE *fp;
241 :
242 1318 : if (php_check_open_basedir((char *)path TSRMLS_CC)) {
243 0 : return NULL;
244 : }
245 1318 : fp = VCWD_FOPEN(path, mode);
246 1318 : if (fp && opened_path) {
247 0 : *opened_path = expand_filepath(path, NULL TSRMLS_CC);
248 : }
249 1318 : return fp;
250 : }
251 : /* }}} */
252 :
253 : /* {{{ php_fopen_primary_script
254 : */
255 : PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC)
256 218 : {
257 : FILE *fp;
258 : #ifndef PHP_WIN32
259 : struct stat st;
260 : #endif
261 : char *path_info, *filename;
262 : int length;
263 :
264 218 : filename = SG(request_info).path_translated;
265 218 : path_info = SG(request_info).request_uri;
266 : #if HAVE_PWD_H
267 218 : if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) {
268 0 : char *s = strchr(path_info + 2, '/');
269 :
270 0 : filename = NULL; /* discard the original filename, it must not be used */
271 0 : if (s) { /* if there is no path name after the file, do not bother */
272 : char user[32]; /* to try open the directory */
273 : struct passwd *pw;
274 : #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
275 : struct passwd pwstruc;
276 : long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
277 : char *pwbuf;
278 :
279 : if (pwbuflen < 1) {
280 : return FAILURE;
281 : }
282 :
283 : pwbuf = emalloc(pwbuflen);
284 : #endif
285 0 : length = s - (path_info + 2);
286 0 : if (length > (int)sizeof(user) - 1) {
287 0 : length = sizeof(user) - 1;
288 : }
289 0 : memcpy(user, path_info + 2, length);
290 0 : user[length] = '\0';
291 : #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
292 : if (getpwnam_r(user, &pwstruc, pwbuf, pwbuflen, &pw)) {
293 : efree(pwbuf);
294 : return FAILURE;
295 : }
296 : #else
297 0 : pw = getpwnam(user);
298 : #endif
299 0 : if (pw && pw->pw_dir) {
300 0 : spprintf(&filename, 0, "%s%c%s%c%s", pw->pw_dir, PHP_DIR_SEPARATOR,
301 : PG(user_dir), PHP_DIR_SEPARATOR, s+1); /* Safe */
302 0 : STR_FREE(SG(request_info).path_translated);
303 0 : SG(request_info).path_translated = filename;
304 : }
305 : #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
306 : efree(pwbuf);
307 : #endif
308 : }
309 : } else
310 : #endif
311 218 : if (PG(doc_root) && path_info) {
312 0 : length = strlen(PG(doc_root));
313 0 : if (IS_ABSOLUTE_PATH(PG(doc_root), length)) {
314 0 : filename = emalloc(length + strlen(path_info) + 2);
315 0 : if (filename) {
316 0 : memcpy(filename, PG(doc_root), length);
317 0 : if (!IS_SLASH(filename[length - 1])) { /* length is never 0 */
318 0 : filename[length++] = PHP_DIR_SEPARATOR;
319 : }
320 0 : if (IS_SLASH(path_info[0])) {
321 0 : length--;
322 : }
323 0 : strcpy(filename + length, path_info);
324 0 : STR_FREE(SG(request_info).path_translated);
325 0 : SG(request_info).path_translated = filename;
326 : }
327 : }
328 : } /* if doc_root && path_info */
329 :
330 218 : if (!filename) {
331 : /* we have to free SG(request_info).path_translated here because
332 : php_destroy_request_info assumes that it will get
333 : freed when the include_names hash is emptied, but
334 : we're not adding it in this case */
335 0 : STR_FREE(SG(request_info).path_translated);
336 0 : SG(request_info).path_translated = NULL;
337 0 : return FAILURE;
338 : }
339 218 : fp = VCWD_FOPEN(filename, "rb");
340 :
341 : #ifndef PHP_WIN32
342 : /* refuse to open anything that is not a regular file */
343 218 : if (fp && (0 > fstat(fileno(fp), &st) || !S_ISREG(st.st_mode))) {
344 0 : fclose(fp);
345 0 : fp = NULL;
346 : }
347 : #endif
348 :
349 218 : if (!fp) {
350 0 : STR_FREE(SG(request_info).path_translated); /* for same reason as above */
351 0 : SG(request_info).path_translated = NULL;
352 0 : return FAILURE;
353 : }
354 :
355 218 : file_handle->opened_path = expand_filepath(filename, NULL TSRMLS_CC);
356 :
357 218 : if (!(SG(options) & SAPI_OPTION_NO_CHDIR)) {
358 218 : VCWD_CHDIR_FILE(filename);
359 : }
360 218 : SG(request_info).path_translated = filename;
361 :
362 218 : file_handle->filename = SG(request_info).path_translated;
363 218 : file_handle->free_filename = 0;
364 218 : file_handle->handle.fp = fp;
365 218 : file_handle->type = ZEND_HANDLE_FP;
366 :
367 218 : return SUCCESS;
368 : }
369 : /* }}} */
370 :
371 : /* {{{ php_fopen_with_path
372 : * Tries to open a file with a PATH-style list of directories.
373 : * If the filename starts with "." or "/", the path is ignored.
374 : */
375 : PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC)
376 440 : {
377 : char *pathbuf, *ptr, *end;
378 : char *exec_fname;
379 : char trypath[MAXPATHLEN];
380 : struct stat sb;
381 : FILE *fp;
382 : int path_length;
383 : int filename_length;
384 : int exec_fname_length;
385 :
386 440 : if (opened_path) {
387 440 : *opened_path = NULL;
388 : }
389 :
390 440 : if(!filename) {
391 0 : return NULL;
392 : }
393 :
394 440 : filename_length = strlen(filename);
395 :
396 : /* Relative path open */
397 440 : if (*filename == '.') {
398 0 : if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
399 0 : return NULL;
400 : }
401 0 : return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
402 : }
403 :
404 : /*
405 : * files in safe_mode_include_dir (or subdir) are excluded from
406 : * safe mode GID/UID checks
407 : */
408 :
409 : /* Absolute path open */
410 440 : if (IS_ABSOLUTE_PATH(filename, filename_length)) {
411 0 : if ((php_check_safe_mode_include_dir(filename TSRMLS_CC)) == 0)
412 : /* filename is in safe_mode_include_dir (or subdir) */
413 0 : return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
414 :
415 0 : if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM)))
416 0 : return NULL;
417 :
418 0 : return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
419 : }
420 :
421 440 : if (!path || (path && !*path)) {
422 0 : if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
423 0 : return NULL;
424 : }
425 0 : return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
426 : }
427 :
428 : /* check in provided path */
429 : /* append the calling scripts' current working directory
430 : * as a fall back case
431 : */
432 440 : if (zend_is_executing(TSRMLS_C)) {
433 0 : exec_fname = zend_get_executed_filename(TSRMLS_C);
434 0 : exec_fname_length = strlen(exec_fname);
435 0 : path_length = strlen(path);
436 :
437 0 : while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
438 0 : if ((exec_fname && exec_fname[0] == '[')
439 : || exec_fname_length<=0) {
440 : /* [no active file] or no path */
441 0 : pathbuf = estrdup(path);
442 : } else {
443 0 : pathbuf = (char *) emalloc(exec_fname_length + path_length +1 +1);
444 0 : memcpy(pathbuf, path, path_length);
445 0 : pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
446 0 : memcpy(pathbuf+path_length+1, exec_fname, exec_fname_length);
447 0 : pathbuf[path_length + exec_fname_length +1] = '\0';
448 : }
449 : } else {
450 440 : pathbuf = estrdup(path);
451 : }
452 :
453 440 : ptr = pathbuf;
454 :
455 2198 : while (ptr && *ptr) {
456 1318 : end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
457 1318 : if (end != NULL) {
458 878 : *end = '\0';
459 878 : end++;
460 : }
461 1318 : snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename);
462 1318 : if (PG(safe_mode)) {
463 0 : if (VCWD_STAT(trypath, &sb) == 0) {
464 : /* file exists ... check permission */
465 0 : if ((php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0) ||
466 : php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM))
467 : /* UID ok, or trypath is in safe_mode_include_dir */
468 0 : fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC);
469 : else
470 0 : fp = NULL;
471 :
472 0 : efree(pathbuf);
473 0 : return fp;
474 : }
475 : }
476 1318 : fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC);
477 1318 : if (fp) {
478 0 : efree(pathbuf);
479 0 : return fp;
480 : }
481 1318 : ptr = end;
482 : } /* end provided path */
483 :
484 440 : efree(pathbuf);
485 440 : return NULL;
486 : }
487 : /* }}} */
488 :
489 : /* {{{ php_strip_url_passwd
490 : */
491 : PHPAPI char *php_strip_url_passwd(char *url)
492 0 : {
493 : register char *p, *url_start;
494 :
495 0 : if (url == NULL) {
496 0 : return "";
497 : }
498 :
499 0 : p = url;
500 :
501 0 : while (*p) {
502 0 : if (*p==':' && *(p+1)=='/' && *(p+2)=='/') {
503 : /* found protocol */
504 0 : url_start = p = p+3;
505 :
506 0 : while (*p) {
507 0 : if (*p=='@') {
508 : int i;
509 :
510 0 : for (i=0; i<3 && url_start<p; i++, url_start++) {
511 0 : *url_start = '.';
512 : }
513 0 : for (; *p; p++) {
514 0 : *url_start++ = *p;
515 : }
516 0 : *url_start=0;
517 0 : break;
518 : }
519 0 : p++;
520 : }
521 0 : return url;
522 : }
523 0 : p++;
524 : }
525 0 : return url;
526 : }
527 : /* }}} */
528 :
529 : /* {{{ expand_filepath
530 : */
531 : PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC)
532 862 : {
533 : cwd_state new_state;
534 : char cwd[MAXPATHLEN];
535 : char *result;
536 :
537 862 : if (IS_ABSOLUTE_PATH(filepath, strlen(filepath))) {
538 577 : cwd[0] = '\0';
539 : } else{
540 285 : result = VCWD_GETCWD(cwd, MAXPATHLEN);
541 285 : if (!result) {
542 0 : cwd[0] = '\0';
543 : }
544 : }
545 :
546 862 : new_state.cwd = strdup(cwd);
547 862 : new_state.cwd_length = strlen(cwd);
548 :
549 862 : if(virtual_file_ex(&new_state, filepath, NULL, CWD_FILEPATH)) {
550 0 : free(new_state.cwd);
551 0 : return NULL;
552 : }
553 :
554 862 : if(real_path) {
555 1 : int copy_len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;
556 1 : memcpy(real_path, new_state.cwd, copy_len);
557 1 : real_path[copy_len]='\0';
558 : } else {
559 861 : real_path = estrndup(new_state.cwd, new_state.cwd_length);
560 : }
561 862 : free(new_state.cwd);
562 :
563 862 : return real_path;
564 : }
565 : /* }}} */
566 :
567 : /*
568 : * Local variables:
569 : * tab-width: 4
570 : * c-basic-offset: 4
571 : * End:
572 : * vim600: sw=4 ts=4 fdm=marker
573 : * vim<600: sw=4 ts=4
574 : */
|