Annotation of embedaddon/php/main/fopen_wrappers.c, revision 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>