Annotation of embedaddon/php/main/fopen_wrappers.c, revision 1.1.1.1
1.1 misho 1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2012 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 321634 2012-01-01 13:15:04Z felipe $ */
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: /* {{{ OnUpdateBaseDir
83: Allows any change to open_basedir setting in during Startup and Shutdown events,
84: or a tightening during activation/runtime/deactivation */
85: PHPAPI ZEND_INI_MH(OnUpdateBaseDir)
86: {
87: char **p, *pathbuf, *ptr, *end;
88:
89: p = &PG(open_basedir);
90:
91: if (stage == PHP_INI_STAGE_STARTUP || stage == PHP_INI_STAGE_SHUTDOWN || stage == PHP_INI_STAGE_ACTIVATE || stage == PHP_INI_STAGE_DEACTIVATE) {
92: /* We're in a PHP_INI_SYSTEM context, no restrictions */
93: *p = new_value;
94: return SUCCESS;
95: }
96:
97: /* Otherwise we're in runtime */
98: if (!*p || !**p) {
99: /* open_basedir not set yet, go ahead and give it a value */
100: *p = new_value;
101: return SUCCESS;
102: }
103:
104: /* Shortcut: When we have a open_basedir and someone tries to unset, we know it'll fail */
105: if (!new_value || !*new_value) {
106: return FAILURE;
107: }
108:
109: /* Is the proposed open_basedir at least as restrictive as the current setting? */
110: ptr = pathbuf = estrdup(new_value);
111: while (ptr && *ptr) {
112: end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
113: if (end != NULL) {
114: *end = '\0';
115: end++;
116: }
117: if (php_check_open_basedir_ex(ptr, 0 TSRMLS_CC) != 0) {
118: /* At least one portion of this open_basedir is less restrictive than the prior one, FAIL */
119: efree(pathbuf);
120: return FAILURE;
121: }
122: ptr = end;
123: }
124: efree(pathbuf);
125:
126: /* Everything checks out, set it */
127: *p = new_value;
128:
129: return SUCCESS;
130: }
131: /* }}} */
132:
133: /* {{{ php_check_specific_open_basedir
134: When open_basedir is not NULL, check if the given filename is located in
135: open_basedir. Returns -1 if error or not in the open_basedir, else 0.
136: When open_basedir is NULL, always return 0.
137: */
138: PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path TSRMLS_DC)
139: {
140: char resolved_name[MAXPATHLEN];
141: char resolved_basedir[MAXPATHLEN];
142: char local_open_basedir[MAXPATHLEN];
143: char path_tmp[MAXPATHLEN];
144: char *path_file;
145: int resolved_basedir_len;
146: int resolved_name_len;
147: int path_len;
148: int nesting_level = 0;
149:
150: /* Special case basedir==".": Use script-directory */
151: if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) {
152: /* Else use the unmodified path */
153: strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir));
154: }
155:
156: path_len = strlen(path);
157: if (path_len > (MAXPATHLEN - 1)) {
158: /* empty and too long paths are invalid */
159: return -1;
160: }
161:
162: /* normalize and expand path */
163: if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
164: return -1;
165: }
166:
167: path_len = strlen(resolved_name);
168: memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
169:
170: while (VCWD_REALPATH(path_tmp, resolved_name) == NULL) {
171: #if defined(PHP_WIN32) || defined(HAVE_SYMLINK)
172: #if defined(PHP_WIN32)
173: if (EG(windows_version_info).dwMajorVersion > 5) {
174: #endif
175: if (nesting_level == 0) {
176: int ret;
177: char buf[MAXPATHLEN];
178:
179: ret = php_sys_readlink(path_tmp, buf, MAXPATHLEN - 1);
180: if (ret < 0) {
181: /* not a broken symlink, move along.. */
182: } else {
183: /* put the real path into the path buffer */
184: memcpy(path_tmp, buf, ret);
185: path_tmp[ret] = '\0';
186: }
187: }
188: #if defined(PHP_WIN32)
189: }
190: #endif
191: #endif
192:
193: #if defined(PHP_WIN32) || defined(NETWARE)
194: path_file = strrchr(path_tmp, DEFAULT_SLASH);
195: if (!path_file) {
196: path_file = strrchr(path_tmp, '/');
197: }
198: #else
199: path_file = strrchr(path_tmp, DEFAULT_SLASH);
200: #endif
201: if (!path_file) {
202: /* none of the path components exist. definitely not in open_basedir.. */
203: return -1;
204: } else {
205: path_len = path_file - path_tmp + 1;
206: #if defined(PHP_WIN32) || defined(NETWARE)
207: if (path_len > 1 && path_tmp[path_len - 2] == ':') {
208: if (path_len != 3) {
209: return -1;
210: }
211: /* this is c:\ */
212: path_tmp[path_len] = '\0';
213: } else {
214: path_tmp[path_len - 1] = '\0';
215: }
216: #else
217: path_tmp[path_len - 1] = '\0';
218: #endif
219: }
220: nesting_level++;
221: }
222:
223: /* Resolve open_basedir to resolved_basedir */
224: if (expand_filepath(local_open_basedir, resolved_basedir TSRMLS_CC) != NULL) {
225: /* Handler for basedirs that end with a / */
226: resolved_basedir_len = strlen(resolved_basedir);
227: #if defined(PHP_WIN32) || defined(NETWARE)
228: if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR || basedir[strlen(basedir) - 1] == '/') {
229: #else
230: if (basedir[strlen(basedir) - 1] == PHP_DIR_SEPARATOR) {
231: #endif
232: if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
233: resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR;
234: resolved_basedir[++resolved_basedir_len] = '\0';
235: }
236: } else {
237: resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR;
238: resolved_basedir[resolved_basedir_len] = '\0';
239: }
240:
241: resolved_name_len = strlen(resolved_name);
242: if (path_tmp[path_len - 1] == PHP_DIR_SEPARATOR) {
243: if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) {
244: resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR;
245: resolved_name[++resolved_name_len] = '\0';
246: }
247: }
248:
249: /* Check the path */
250: #if defined(PHP_WIN32) || defined(NETWARE)
251: if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
252: #else
253: if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
254: #endif
255: if (resolved_name_len > resolved_basedir_len &&
256: resolved_name[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
257: return -1;
258: } else {
259: /* File is in the right directory */
260: return 0;
261: }
262: } else {
263: /* /openbasedir/ and /openbasedir are the same directory */
264: if (resolved_basedir_len == (resolved_name_len + 1) && resolved_basedir[resolved_basedir_len - 1] == PHP_DIR_SEPARATOR) {
265: #if defined(PHP_WIN32) || defined(NETWARE)
266: if (strncasecmp(resolved_basedir, resolved_name, resolved_name_len) == 0) {
267: #else
268: if (strncmp(resolved_basedir, resolved_name, resolved_name_len) == 0) {
269: #endif
270: return 0;
271: }
272: }
273: return -1;
274: }
275: } else {
276: /* Unable to resolve the real path, return -1 */
277: return -1;
278: }
279: }
280: /* }}} */
281:
282: PHPAPI int php_check_open_basedir(const char *path TSRMLS_DC)
283: {
284: return php_check_open_basedir_ex(path, 1 TSRMLS_CC);
285: }
286:
287: /* {{{ php_check_open_basedir
288: */
289: PHPAPI int php_check_open_basedir_ex(const char *path, int warn TSRMLS_DC)
290: {
291: /* Only check when open_basedir is available */
292: if (PG(open_basedir) && *PG(open_basedir)) {
293: char *pathbuf;
294: char *ptr;
295: char *end;
296:
297: /* Check if the path is too long so we can give a more useful error
298: * message. */
299: if (strlen(path) > (MAXPATHLEN - 1)) {
300: php_error_docref(NULL TSRMLS_CC, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path);
301: errno = EINVAL;
302: return -1;
303: }
304:
305: pathbuf = estrdup(PG(open_basedir));
306:
307: ptr = pathbuf;
308:
309: while (ptr && *ptr) {
310: end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
311: if (end != NULL) {
312: *end = '\0';
313: end++;
314: }
315:
316: if (php_check_specific_open_basedir(ptr, path TSRMLS_CC) == 0) {
317: efree(pathbuf);
318: return 0;
319: }
320:
321: ptr = end;
322: }
323: if (warn) {
324: php_error_docref(NULL TSRMLS_CC, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
325: }
326: efree(pathbuf);
327: errno = EPERM; /* we deny permission to open it */
328: return -1;
329: }
330:
331: /* Nothing to check... */
332: return 0;
333: }
334: /* }}} */
335:
336: /* {{{ php_check_safe_mode_include_dir
337: */
338: PHPAPI int php_check_safe_mode_include_dir(const char *path TSRMLS_DC)
339: {
340: if (PG(safe_mode)) {
341: if (PG(safe_mode_include_dir) && *PG(safe_mode_include_dir)) {
342: char *pathbuf;
343: char *ptr;
344: char *end;
345: char resolved_name[MAXPATHLEN];
346:
347: /* Resolve the real path into resolved_name */
348: if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
349: return -1;
350: }
351: pathbuf = estrdup(PG(safe_mode_include_dir));
352: ptr = pathbuf;
353:
354: while (ptr && *ptr) {
355: end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
356: if (end != NULL) {
357: *end = '\0';
358: end++;
359: }
360:
361: /* Check the path */
362: #ifdef PHP_WIN32
363: if (strncasecmp(ptr, resolved_name, strlen(ptr)) == 0)
364: #else
365: if (strncmp(ptr, resolved_name, strlen(ptr)) == 0)
366: #endif
367: {
368: /* File is in the right directory */
369: efree(pathbuf);
370: return 0;
371: }
372:
373: ptr = end;
374: }
375: efree(pathbuf);
376: }
377: return -1;
378: }
379:
380: /* Nothing to check... */
381: return 0;
382: }
383: /* }}} */
384:
385: /* {{{ php_fopen_and_set_opened_path
386: */
387: static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, char **opened_path TSRMLS_DC)
388: {
389: FILE *fp;
390:
391: if (php_check_open_basedir((char *)path TSRMLS_CC)) {
392: return NULL;
393: }
394: fp = VCWD_FOPEN(path, mode);
395: if (fp && opened_path) {
396: *opened_path = expand_filepath(path, NULL TSRMLS_CC);
397: }
398: return fp;
399: }
400: /* }}} */
401:
402: /* {{{ php_fopen_primary_script
403: */
404: PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle TSRMLS_DC)
405: {
406: FILE *fp;
407: #ifndef PHP_WIN32
408: struct stat st;
409: #endif
410: char *path_info;
411: char *filename = NULL;
412: char *resolved_path = NULL;
413: int length;
414:
415: path_info = SG(request_info).request_uri;
416: #if HAVE_PWD_H
417: if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) {
418: char *s = strchr(path_info + 2, '/');
419:
420: if (s) { /* if there is no path name after the file, do not bother */
421: char user[32]; /* to try open the directory */
422: struct passwd *pw;
423: #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
424: struct passwd pwstruc;
425: long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
426: char *pwbuf;
427:
428: if (pwbuflen < 1) {
429: return FAILURE;
430: }
431:
432: pwbuf = emalloc(pwbuflen);
433: #endif
434: length = s - (path_info + 2);
435: if (length > (int)sizeof(user) - 1) {
436: length = sizeof(user) - 1;
437: }
438: memcpy(user, path_info + 2, length);
439: user[length] = '\0';
440: #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
441: if (getpwnam_r(user, &pwstruc, pwbuf, pwbuflen, &pw)) {
442: efree(pwbuf);
443: return FAILURE;
444: }
445: #else
446: pw = getpwnam(user);
447: #endif
448: if (pw && pw->pw_dir) {
449: spprintf(&filename, 0, "%s%c%s%c%s", pw->pw_dir, PHP_DIR_SEPARATOR, PG(user_dir), PHP_DIR_SEPARATOR, s + 1); /* Safe */
450: } else {
451: filename = SG(request_info).path_translated;
452: }
453: #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
454: efree(pwbuf);
455: #endif
456: }
457: } else
458: #endif
459: if (PG(doc_root) && path_info && (length = strlen(PG(doc_root))) &&
460: IS_ABSOLUTE_PATH(PG(doc_root), length)) {
461: int path_len = strlen(path_info);
462: filename = emalloc(length + path_len + 2);
463: if (filename) {
464: memcpy(filename, PG(doc_root), length);
465: if (!IS_SLASH(filename[length - 1])) { /* length is never 0 */
466: filename[length++] = PHP_DIR_SEPARATOR;
467: }
468: if (IS_SLASH(path_info[0])) {
469: length--;
470: }
471: strncpy(filename + length, path_info, path_len + 1);
472: }
473: } else {
474: filename = SG(request_info).path_translated;
475: }
476:
477:
478: if (filename) {
479: resolved_path = zend_resolve_path(filename, strlen(filename) TSRMLS_CC);
480: }
481:
482: if (!resolved_path) {
483: if (SG(request_info).path_translated != filename) {
484: STR_FREE(filename);
485: }
486: /* we have to free SG(request_info).path_translated here because
487: * php_destroy_request_info assumes that it will get
488: * freed when the include_names hash is emptied, but
489: * we're not adding it in this case */
490: STR_FREE(SG(request_info).path_translated);
491: SG(request_info).path_translated = NULL;
492: return FAILURE;
493: }
494: fp = VCWD_FOPEN(resolved_path, "rb");
495:
496: #ifndef PHP_WIN32
497: /* refuse to open anything that is not a regular file */
498: if (fp && (0 > fstat(fileno(fp), &st) || !S_ISREG(st.st_mode))) {
499: fclose(fp);
500: fp = NULL;
501: }
502: #endif
503:
504: if (!fp) {
505: if (SG(request_info).path_translated != filename) {
506: STR_FREE(filename);
507: }
508: STR_FREE(SG(request_info).path_translated); /* for same reason as above */
509: SG(request_info).path_translated = NULL;
510: return FAILURE;
511: }
512:
513: file_handle->opened_path = resolved_path;
514:
515: if (SG(request_info).path_translated != filename) {
516: STR_FREE(SG(request_info).path_translated); /* for same reason as above */
517: SG(request_info).path_translated = filename;
518: }
519:
520: file_handle->filename = SG(request_info).path_translated;
521: file_handle->free_filename = 0;
522: file_handle->handle.fp = fp;
523: file_handle->type = ZEND_HANDLE_FP;
524:
525: return SUCCESS;
526: }
527: /* }}} */
528:
529: /* {{{ php_resolve_path
530: * Returns the realpath for given filename according to include path
531: */
532: PHPAPI char *php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC)
533: {
534: char resolved_path[MAXPATHLEN];
535: char trypath[MAXPATHLEN];
536: const char *ptr, *end, *p;
537: char *actual_path;
538: php_stream_wrapper *wrapper;
539:
540: if (!filename) {
541: return NULL;
542: }
543:
544: if (strlen(filename) != filename_length) {
545: return NULL;
546: }
547:
548: /* Don't resolve paths which contain protocol (except of file://) */
549: for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
550: if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) {
551: wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
552: if (wrapper == &php_plain_files_wrapper) {
553: if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
554: return estrdup(resolved_path);
555: }
556: }
557: return NULL;
558: }
559:
560: if ((*filename == '.' &&
561: (IS_SLASH(filename[1]) ||
562: ((filename[1] == '.') && IS_SLASH(filename[2])))) ||
563: IS_ABSOLUTE_PATH(filename, filename_length) ||
564: !path ||
565: !*path) {
566: if (tsrm_realpath(filename, resolved_path TSRMLS_CC)) {
567: return estrdup(resolved_path);
568: } else {
569: return NULL;
570: }
571: }
572:
573: ptr = path;
574: while (ptr && *ptr) {
575: /* Check for stream wrapper */
576: int is_stream_wrapper = 0;
577:
578: for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
579: if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) {
580: /* .:// or ..:// is not a stream wrapper */
581: if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) {
582: p += 3;
583: is_stream_wrapper = 1;
584: }
585: }
586: end = strchr(p, DEFAULT_DIR_SEPARATOR);
587: if (end) {
588: if ((end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) {
589: ptr = end + 1;
590: continue;
591: }
592: memcpy(trypath, ptr, end-ptr);
593: trypath[end-ptr] = '/';
594: memcpy(trypath+(end-ptr)+1, filename, filename_length+1);
595: ptr = end+1;
596: } else {
597: int len = strlen(ptr);
598:
599: if (len + 1 + filename_length + 1 >= MAXPATHLEN) {
600: break;
601: }
602: memcpy(trypath, ptr, len);
603: trypath[len] = '/';
604: memcpy(trypath+len+1, filename, filename_length+1);
605: ptr = NULL;
606: }
607: actual_path = trypath;
608: if (is_stream_wrapper) {
609: wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
610: if (!wrapper) {
611: continue;
612: } else if (wrapper != &php_plain_files_wrapper) {
613: if (wrapper->wops->url_stat) {
614: php_stream_statbuf ssb;
615:
616: if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) {
617: return estrdup(trypath);
618: }
619: }
620: continue;
621: }
622: }
623: if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
624: return estrdup(resolved_path);
625: }
626: } /* end provided path */
627:
628: /* check in calling scripts' current working directory as a fall back case
629: */
630: if (zend_is_executing(TSRMLS_C)) {
631: char *exec_fname = zend_get_executed_filename(TSRMLS_C);
632: int exec_fname_length = strlen(exec_fname);
633:
634: while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
635: if (exec_fname && exec_fname[0] != '[' &&
636: exec_fname_length > 0 &&
637: exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
638: memcpy(trypath, exec_fname, exec_fname_length + 1);
639: memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
640: actual_path = trypath;
641:
642: /* Check for stream wrapper */
643: for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
644: if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) {
645: wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE TSRMLS_CC);
646: if (!wrapper) {
647: return NULL;
648: } else if (wrapper != &php_plain_files_wrapper) {
649: if (wrapper->wops->url_stat) {
650: php_stream_statbuf ssb;
651:
652: if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, 0, &ssb, NULL TSRMLS_CC)) {
653: return estrdup(trypath);
654: }
655: }
656: return NULL;
657: }
658: }
659:
660: if (tsrm_realpath(actual_path, resolved_path TSRMLS_CC)) {
661: return estrdup(resolved_path);
662: }
663: }
664: }
665:
666: return NULL;
667: }
668: /* }}} */
669:
670: /* {{{ php_fopen_with_path
671: * Tries to open a file with a PATH-style list of directories.
672: * If the filename starts with "." or "/", the path is ignored.
673: */
674: PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, char **opened_path TSRMLS_DC)
675: {
676: char *pathbuf, *ptr, *end;
677: char *exec_fname;
678: char trypath[MAXPATHLEN];
679: struct stat sb;
680: FILE *fp;
681: int path_length;
682: int filename_length;
683: int exec_fname_length;
684:
685: if (opened_path) {
686: *opened_path = NULL;
687: }
688:
689: if (!filename) {
690: return NULL;
691: }
692:
693: filename_length = strlen(filename);
694:
695: /* Relative path open */
696: if (*filename == '.') {
697: if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
698: return NULL;
699: }
700: return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
701: }
702:
703: /*
704: * files in safe_mode_include_dir (or subdir) are excluded from
705: * safe mode GID/UID checks
706: */
707:
708: /* Absolute path open */
709: if (IS_ABSOLUTE_PATH(filename, filename_length)) {
710: if (php_check_safe_mode_include_dir(filename TSRMLS_CC) == 0) {
711: /* filename is in safe_mode_include_dir (or subdir) */
712: return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
713: }
714: if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
715: return NULL;
716: }
717: return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
718: }
719:
720: if (!path || (path && !*path)) {
721: if (PG(safe_mode) && (!php_checkuid(filename, mode, CHECKUID_CHECK_MODE_PARAM))) {
722: return NULL;
723: }
724: return php_fopen_and_set_opened_path(filename, mode, opened_path TSRMLS_CC);
725: }
726:
727: /* check in provided path */
728: /* append the calling scripts' current working directory
729: * as a fall back case
730: */
731: if (zend_is_executing(TSRMLS_C)) {
732: exec_fname = zend_get_executed_filename(TSRMLS_C);
733: exec_fname_length = strlen(exec_fname);
734: path_length = strlen(path);
735:
736: while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
737: if ((exec_fname && exec_fname[0] == '[') || exec_fname_length <= 0) {
738: /* [no active file] or no path */
739: pathbuf = estrdup(path);
740: } else {
741: pathbuf = (char *) emalloc(exec_fname_length + path_length + 1 + 1);
742: memcpy(pathbuf, path, path_length);
743: pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
744: memcpy(pathbuf + path_length + 1, exec_fname, exec_fname_length);
745: pathbuf[path_length + exec_fname_length + 1] = '\0';
746: }
747: } else {
748: pathbuf = estrdup(path);
749: }
750:
751: ptr = pathbuf;
752:
753: while (ptr && *ptr) {
754: end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
755: if (end != NULL) {
756: *end = '\0';
757: end++;
758: }
759: if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
760: php_error_docref(NULL TSRMLS_CC, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
761: }
762: if (PG(safe_mode)) {
763: if (VCWD_STAT(trypath, &sb) == 0) {
764: /* file exists ... check permission */
765: if (php_check_safe_mode_include_dir(trypath TSRMLS_CC) == 0 ||
766: php_checkuid(trypath, mode, CHECKUID_CHECK_MODE_PARAM)
767: ) {
768: /* UID ok, or trypath is in safe_mode_include_dir */
769: fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC);
770: } else {
771: fp = NULL;
772: }
773: efree(pathbuf);
774: return fp;
775: }
776: }
777: fp = php_fopen_and_set_opened_path(trypath, mode, opened_path TSRMLS_CC);
778: if (fp) {
779: efree(pathbuf);
780: return fp;
781: }
782: ptr = end;
783: } /* end provided path */
784:
785: efree(pathbuf);
786: return NULL;
787: }
788: /* }}} */
789:
790: /* {{{ php_strip_url_passwd
791: */
792: PHPAPI char *php_strip_url_passwd(char *url)
793: {
794: register char *p, *url_start;
795:
796: if (url == NULL) {
797: return "";
798: }
799:
800: p = url;
801:
802: while (*p) {
803: if (*p == ':' && *(p + 1) == '/' && *(p + 2) == '/') {
804: /* found protocol */
805: url_start = p = p + 3;
806:
807: while (*p) {
808: if (*p == '@') {
809: int i;
810:
811: for (i = 0; i < 3 && url_start < p; i++, url_start++) {
812: *url_start = '.';
813: }
814: for (; *p; p++) {
815: *url_start++ = *p;
816: }
817: *url_start=0;
818: break;
819: }
820: p++;
821: }
822: return url;
823: }
824: p++;
825: }
826: return url;
827: }
828: /* }}} */
829:
830: /* {{{ expand_filepath
831: */
832: PHPAPI char *expand_filepath(const char *filepath, char *real_path TSRMLS_DC)
833: {
834: return expand_filepath_ex(filepath, real_path, NULL, 0 TSRMLS_CC);
835: }
836: /* }}} */
837:
838: /* {{{ expand_filepath_ex
839: */
840: PHPAPI char *expand_filepath_ex(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len TSRMLS_DC)
841: {
842: cwd_state new_state;
843: char cwd[MAXPATHLEN];
844: int copy_len;
845:
846: if (!filepath[0]) {
847: return NULL;
848: } else if (IS_ABSOLUTE_PATH(filepath, strlen(filepath))) {
849: cwd[0] = '\0';
850: } else {
851: const char *iam = SG(request_info).path_translated;
852: const char *result;
853: if (relative_to) {
854: if (relative_to_len > MAXPATHLEN-1U) {
855: return NULL;
856: }
857: result = relative_to;
858: memcpy(cwd, relative_to, relative_to_len+1U);
859: } else {
860: result = VCWD_GETCWD(cwd, MAXPATHLEN);
861: }
862:
863: if (!result && (iam != filepath)) {
864: int fdtest = -1;
865:
866: fdtest = VCWD_OPEN(filepath, O_RDONLY);
867: if (fdtest != -1) {
868: /* return a relative file path if for any reason
869: * we cannot cannot getcwd() and the requested,
870: * relatively referenced file is accessible */
871: copy_len = strlen(filepath) > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : strlen(filepath);
872: real_path = estrndup(filepath, copy_len);
873: close(fdtest);
874: return real_path;
875: } else {
876: cwd[0] = '\0';
877: }
878: } else if (!result) {
879: cwd[0] = '\0';
880: }
881: }
882:
883: new_state.cwd = strdup(cwd);
884: new_state.cwd_length = strlen(cwd);
885:
886: if (virtual_file_ex(&new_state, filepath, NULL, CWD_FILEPATH)) {
887: free(new_state.cwd);
888: return NULL;
889: }
890:
891: if (real_path) {
892: copy_len = new_state.cwd_length > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : new_state.cwd_length;
893: memcpy(real_path, new_state.cwd, copy_len);
894: real_path[copy_len] = '\0';
895: } else {
896: real_path = estrndup(new_state.cwd, new_state.cwd_length);
897: }
898: free(new_state.cwd);
899:
900: return real_path;
901: }
902: /* }}} */
903:
904: /*
905: * Local variables:
906: * tab-width: 4
907: * c-basic-offset: 4
908: * End:
909: * vim600: sw=4 ts=4 fdm=marker
910: * vim<600: sw=4 ts=4
911: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>