PageRenderTime 47ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/main/fopen_wrappers.c

http://github.com/php/php-src
C | 828 lines | 626 code | 92 blank | 110 comment | 280 complexity | 1a7aca83bbfc4280f2f3947f11a46f71 MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | http://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
  14. | Jim Winstead <jimw@php.net> |
  15. +----------------------------------------------------------------------+
  16. */
  17. /* {{{ includes
  18. */
  19. #include "php.h"
  20. #include "php_globals.h"
  21. #include "SAPI.h"
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <errno.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <fcntl.h>
  28. #ifdef PHP_WIN32
  29. #define O_RDONLY _O_RDONLY
  30. #include "win32/param.h"
  31. #else
  32. #include <sys/param.h>
  33. #endif
  34. #include "ext/standard/head.h"
  35. #include "ext/standard/php_standard.h"
  36. #include "zend_compile.h"
  37. #include "php_network.h"
  38. #if HAVE_PWD_H
  39. #include <pwd.h>
  40. #endif
  41. #include <sys/types.h>
  42. #if HAVE_SYS_SOCKET_H
  43. #include <sys/socket.h>
  44. #endif
  45. #ifdef PHP_WIN32
  46. #include <winsock2.h>
  47. #else
  48. #include <netinet/in.h>
  49. #include <netdb.h>
  50. #if HAVE_ARPA_INET_H
  51. #include <arpa/inet.h>
  52. #endif
  53. #endif
  54. #if defined(PHP_WIN32) || defined(__riscos__)
  55. #undef AF_UNIX
  56. #endif
  57. #if defined(AF_UNIX)
  58. #include <sys/un.h>
  59. #endif
  60. /* }}} */
  61. /* {{{ OnUpdateBaseDir
  62. Allows any change to open_basedir setting in during Startup and Shutdown events,
  63. or a tightening during activation/runtime/deactivation */
  64. PHPAPI ZEND_INI_MH(OnUpdateBaseDir)
  65. {
  66. char **p = (char **) ZEND_INI_GET_ADDR();
  67. char *pathbuf, *ptr, *end;
  68. if (stage == PHP_INI_STAGE_STARTUP || stage == PHP_INI_STAGE_SHUTDOWN || stage == PHP_INI_STAGE_ACTIVATE || stage == PHP_INI_STAGE_DEACTIVATE) {
  69. /* We're in a PHP_INI_SYSTEM context, no restrictions */
  70. *p = new_value ? ZSTR_VAL(new_value) : NULL;
  71. return SUCCESS;
  72. }
  73. /* Otherwise we're in runtime */
  74. if (!*p || !**p) {
  75. /* open_basedir not set yet, go ahead and give it a value */
  76. *p = ZSTR_VAL(new_value);
  77. return SUCCESS;
  78. }
  79. /* Shortcut: When we have a open_basedir and someone tries to unset, we know it'll fail */
  80. if (!new_value || !*ZSTR_VAL(new_value)) {
  81. return FAILURE;
  82. }
  83. /* Is the proposed open_basedir at least as restrictive as the current setting? */
  84. ptr = pathbuf = estrdup(ZSTR_VAL(new_value));
  85. while (ptr && *ptr) {
  86. end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
  87. if (end != NULL) {
  88. *end = '\0';
  89. end++;
  90. }
  91. if (php_check_open_basedir_ex(ptr, 0) != 0) {
  92. /* At least one portion of this open_basedir is less restrictive than the prior one, FAIL */
  93. efree(pathbuf);
  94. return FAILURE;
  95. }
  96. ptr = end;
  97. }
  98. efree(pathbuf);
  99. /* Everything checks out, set it */
  100. *p = ZSTR_VAL(new_value);
  101. return SUCCESS;
  102. }
  103. /* }}} */
  104. /* {{{ php_check_specific_open_basedir
  105. When open_basedir is not NULL, check if the given filename is located in
  106. open_basedir. Returns -1 if error or not in the open_basedir, else 0.
  107. When open_basedir is NULL, always return 0.
  108. */
  109. PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path)
  110. {
  111. char resolved_name[MAXPATHLEN];
  112. char resolved_basedir[MAXPATHLEN];
  113. char local_open_basedir[MAXPATHLEN];
  114. char path_tmp[MAXPATHLEN];
  115. char *path_file;
  116. size_t resolved_basedir_len;
  117. size_t resolved_name_len;
  118. size_t path_len;
  119. int nesting_level = 0;
  120. /* Special case basedir==".": Use script-directory */
  121. if (strcmp(basedir, ".") || !VCWD_GETCWD(local_open_basedir, MAXPATHLEN)) {
  122. /* Else use the unmodified path */
  123. strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir));
  124. }
  125. path_len = strlen(path);
  126. if (path_len > (MAXPATHLEN - 1)) {
  127. /* empty and too long paths are invalid */
  128. return -1;
  129. }
  130. /* normalize and expand path */
  131. if (expand_filepath(path, resolved_name) == NULL) {
  132. return -1;
  133. }
  134. path_len = strlen(resolved_name);
  135. memcpy(path_tmp, resolved_name, path_len + 1); /* safe */
  136. while (VCWD_REALPATH(path_tmp, resolved_name) == NULL) {
  137. #if defined(PHP_WIN32) || defined(HAVE_SYMLINK)
  138. if (nesting_level == 0) {
  139. ssize_t ret;
  140. char buf[MAXPATHLEN];
  141. ret = php_sys_readlink(path_tmp, buf, MAXPATHLEN - 1);
  142. if (ret == -1) {
  143. /* not a broken symlink, move along.. */
  144. } else {
  145. /* put the real path into the path buffer */
  146. memcpy(path_tmp, buf, ret);
  147. path_tmp[ret] = '\0';
  148. }
  149. }
  150. #endif
  151. #ifdef PHP_WIN32
  152. path_file = strrchr(path_tmp, DEFAULT_SLASH);
  153. if (!path_file) {
  154. path_file = strrchr(path_tmp, '/');
  155. }
  156. #else
  157. path_file = strrchr(path_tmp, DEFAULT_SLASH);
  158. #endif
  159. if (!path_file) {
  160. /* none of the path components exist. definitely not in open_basedir.. */
  161. return -1;
  162. } else {
  163. path_len = path_file - path_tmp + 1;
  164. #ifdef PHP_WIN32
  165. if (path_len > 1 && path_tmp[path_len - 2] == ':') {
  166. if (path_len != 3) {
  167. return -1;
  168. }
  169. /* this is c:\ */
  170. path_tmp[path_len] = '\0';
  171. } else {
  172. path_tmp[path_len - 1] = '\0';
  173. }
  174. #else
  175. path_tmp[path_len - 1] = '\0';
  176. #endif
  177. }
  178. nesting_level++;
  179. }
  180. /* Resolve open_basedir to resolved_basedir */
  181. if (expand_filepath(local_open_basedir, resolved_basedir) != NULL) {
  182. size_t basedir_len = strlen(basedir);
  183. /* Handler for basedirs that end with a / */
  184. resolved_basedir_len = strlen(resolved_basedir);
  185. #ifdef PHP_WIN32
  186. if (basedir[basedir_len - 1] == PHP_DIR_SEPARATOR || basedir[basedir_len - 1] == '/') {
  187. #else
  188. if (basedir[basedir_len - 1] == PHP_DIR_SEPARATOR) {
  189. #endif
  190. if (resolved_basedir[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
  191. resolved_basedir[resolved_basedir_len] = PHP_DIR_SEPARATOR;
  192. resolved_basedir[++resolved_basedir_len] = '\0';
  193. }
  194. } else {
  195. resolved_basedir[resolved_basedir_len++] = PHP_DIR_SEPARATOR;
  196. resolved_basedir[resolved_basedir_len] = '\0';
  197. }
  198. resolved_name_len = strlen(resolved_name);
  199. if (path_tmp[path_len - 1] == PHP_DIR_SEPARATOR) {
  200. if (resolved_name[resolved_name_len - 1] != PHP_DIR_SEPARATOR) {
  201. resolved_name[resolved_name_len] = PHP_DIR_SEPARATOR;
  202. resolved_name[++resolved_name_len] = '\0';
  203. }
  204. }
  205. /* Check the path */
  206. #ifdef PHP_WIN32
  207. if (strncasecmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
  208. #else
  209. if (strncmp(resolved_basedir, resolved_name, resolved_basedir_len) == 0) {
  210. #endif
  211. if (resolved_name_len > resolved_basedir_len &&
  212. resolved_name[resolved_basedir_len - 1] != PHP_DIR_SEPARATOR) {
  213. return -1;
  214. } else {
  215. /* File is in the right directory */
  216. return 0;
  217. }
  218. } else {
  219. /* /openbasedir/ and /openbasedir are the same directory */
  220. if (resolved_basedir_len == (resolved_name_len + 1) && resolved_basedir[resolved_basedir_len - 1] == PHP_DIR_SEPARATOR) {
  221. #ifdef PHP_WIN32
  222. if (strncasecmp(resolved_basedir, resolved_name, resolved_name_len) == 0) {
  223. #else
  224. if (strncmp(resolved_basedir, resolved_name, resolved_name_len) == 0) {
  225. #endif
  226. return 0;
  227. }
  228. }
  229. return -1;
  230. }
  231. } else {
  232. /* Unable to resolve the real path, return -1 */
  233. return -1;
  234. }
  235. }
  236. /* }}} */
  237. PHPAPI int php_check_open_basedir(const char *path)
  238. {
  239. return php_check_open_basedir_ex(path, 1);
  240. }
  241. /* {{{ php_check_open_basedir
  242. */
  243. PHPAPI int php_check_open_basedir_ex(const char *path, int warn)
  244. {
  245. /* Only check when open_basedir is available */
  246. if (PG(open_basedir) && *PG(open_basedir)) {
  247. char *pathbuf;
  248. char *ptr;
  249. char *end;
  250. /* Check if the path is too long so we can give a more useful error
  251. * message. */
  252. if (strlen(path) > (MAXPATHLEN - 1)) {
  253. php_error_docref(NULL, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path);
  254. errno = EINVAL;
  255. return -1;
  256. }
  257. pathbuf = estrdup(PG(open_basedir));
  258. ptr = pathbuf;
  259. while (ptr && *ptr) {
  260. end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
  261. if (end != NULL) {
  262. *end = '\0';
  263. end++;
  264. }
  265. if (php_check_specific_open_basedir(ptr, path) == 0) {
  266. efree(pathbuf);
  267. return 0;
  268. }
  269. ptr = end;
  270. }
  271. if (warn) {
  272. php_error_docref(NULL, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
  273. }
  274. efree(pathbuf);
  275. errno = EPERM; /* we deny permission to open it */
  276. return -1;
  277. }
  278. /* Nothing to check... */
  279. return 0;
  280. }
  281. /* }}} */
  282. /* {{{ php_fopen_and_set_opened_path
  283. */
  284. static FILE *php_fopen_and_set_opened_path(const char *path, const char *mode, zend_string **opened_path)
  285. {
  286. FILE *fp;
  287. if (php_check_open_basedir((char *)path)) {
  288. return NULL;
  289. }
  290. fp = VCWD_FOPEN(path, mode);
  291. if (fp && opened_path) {
  292. //TODO :avoid reallocation
  293. char *tmp = expand_filepath_with_mode(path, NULL, NULL, 0, CWD_EXPAND);
  294. if (tmp) {
  295. *opened_path = zend_string_init(tmp, strlen(tmp), 0);
  296. efree(tmp);
  297. }
  298. }
  299. return fp;
  300. }
  301. /* }}} */
  302. /* {{{ php_fopen_primary_script
  303. */
  304. PHPAPI int php_fopen_primary_script(zend_file_handle *file_handle)
  305. {
  306. char *path_info;
  307. char *filename = NULL;
  308. zend_string *resolved_path = NULL;
  309. size_t length;
  310. zend_bool orig_display_errors;
  311. path_info = SG(request_info).request_uri;
  312. #if HAVE_PWD_H
  313. if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) {
  314. char *s = strchr(path_info + 2, '/');
  315. if (s) { /* if there is no path name after the file, do not bother */
  316. char user[32]; /* to try open the directory */
  317. struct passwd *pw;
  318. #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
  319. struct passwd pwstruc;
  320. long pwbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
  321. char *pwbuf;
  322. if (pwbuflen < 1) {
  323. return FAILURE;
  324. }
  325. pwbuf = emalloc(pwbuflen);
  326. #endif
  327. length = s - (path_info + 2);
  328. if (length > sizeof(user) - 1) {
  329. length = sizeof(user) - 1;
  330. }
  331. memcpy(user, path_info + 2, length);
  332. user[length] = '\0';
  333. #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
  334. if (getpwnam_r(user, &pwstruc, pwbuf, pwbuflen, &pw)) {
  335. efree(pwbuf);
  336. return FAILURE;
  337. }
  338. #else
  339. pw = getpwnam(user);
  340. #endif
  341. if (pw && pw->pw_dir) {
  342. spprintf(&filename, 0, "%s%c%s%c%s", pw->pw_dir, PHP_DIR_SEPARATOR, PG(user_dir), PHP_DIR_SEPARATOR, s + 1); /* Safe */
  343. } else {
  344. filename = SG(request_info).path_translated;
  345. }
  346. #if defined(ZTS) && defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
  347. efree(pwbuf);
  348. #endif
  349. }
  350. } else
  351. #endif
  352. if (PG(doc_root) && path_info && (length = strlen(PG(doc_root))) &&
  353. IS_ABSOLUTE_PATH(PG(doc_root), length)) {
  354. size_t path_len = strlen(path_info);
  355. filename = emalloc(length + path_len + 2);
  356. memcpy(filename, PG(doc_root), length);
  357. if (!IS_SLASH(filename[length - 1])) { /* length is never 0 */
  358. filename[length++] = PHP_DIR_SEPARATOR;
  359. }
  360. if (IS_SLASH(path_info[0])) {
  361. length--;
  362. }
  363. strncpy(filename + length, path_info, path_len + 1);
  364. } else {
  365. filename = SG(request_info).path_translated;
  366. }
  367. if (filename) {
  368. resolved_path = zend_resolve_path(filename, strlen(filename));
  369. }
  370. if (!resolved_path) {
  371. if (SG(request_info).path_translated != filename) {
  372. if (filename) {
  373. efree(filename);
  374. }
  375. }
  376. /* we have to free SG(request_info).path_translated here because
  377. * php_destroy_request_info assumes that it will get
  378. * freed when the include_names hash is emptied, but
  379. * we're not adding it in this case */
  380. if (SG(request_info).path_translated) {
  381. efree(SG(request_info).path_translated);
  382. SG(request_info).path_translated = NULL;
  383. }
  384. return FAILURE;
  385. }
  386. zend_string_release_ex(resolved_path, 0);
  387. orig_display_errors = PG(display_errors);
  388. PG(display_errors) = 0;
  389. if (zend_stream_open(filename, file_handle) == FAILURE) {
  390. PG(display_errors) = orig_display_errors;
  391. if (SG(request_info).path_translated != filename) {
  392. if (filename) {
  393. efree(filename);
  394. }
  395. }
  396. if (SG(request_info).path_translated) {
  397. efree(SG(request_info).path_translated);
  398. SG(request_info).path_translated = NULL;
  399. }
  400. return FAILURE;
  401. }
  402. PG(display_errors) = orig_display_errors;
  403. if (SG(request_info).path_translated != filename) {
  404. if (SG(request_info).path_translated) {
  405. efree(SG(request_info).path_translated);
  406. }
  407. SG(request_info).path_translated = filename;
  408. }
  409. return SUCCESS;
  410. }
  411. /* }}} */
  412. /* {{{ php_resolve_path
  413. * Returns the realpath for given filename according to include path
  414. */
  415. PHPAPI zend_string *php_resolve_path(const char *filename, size_t filename_length, const char *path)
  416. {
  417. char resolved_path[MAXPATHLEN];
  418. char trypath[MAXPATHLEN];
  419. const char *ptr, *end, *p;
  420. const char *actual_path;
  421. php_stream_wrapper *wrapper;
  422. zend_string *exec_filename;
  423. if (!filename || CHECK_NULL_PATH(filename, filename_length)) {
  424. return NULL;
  425. }
  426. /* Don't resolve paths which contain protocol (except of file://) */
  427. for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
  428. if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) {
  429. wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE);
  430. if (wrapper == &php_plain_files_wrapper) {
  431. if (tsrm_realpath(actual_path, resolved_path)) {
  432. return zend_string_init(resolved_path, strlen(resolved_path), 0);
  433. }
  434. }
  435. return NULL;
  436. }
  437. if ((*filename == '.' &&
  438. (IS_SLASH(filename[1]) ||
  439. ((filename[1] == '.') && IS_SLASH(filename[2])))) ||
  440. IS_ABSOLUTE_PATH(filename, filename_length) ||
  441. #ifdef PHP_WIN32
  442. /* This should count as an absolute local path as well, however
  443. IS_ABSOLUTE_PATH doesn't care about this path form till now. It
  444. might be a big thing to extend, thus just a local handling for
  445. now. */
  446. filename_length >=2 && IS_SLASH(filename[0]) && !IS_SLASH(filename[1]) ||
  447. #endif
  448. !path ||
  449. !*path) {
  450. if (tsrm_realpath(filename, resolved_path)) {
  451. return zend_string_init(resolved_path, strlen(resolved_path), 0);
  452. } else {
  453. return NULL;
  454. }
  455. }
  456. ptr = path;
  457. while (ptr && *ptr) {
  458. /* Check for stream wrapper */
  459. int is_stream_wrapper = 0;
  460. for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
  461. if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) {
  462. /* .:// or ..:// is not a stream wrapper */
  463. if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) {
  464. p += 3;
  465. is_stream_wrapper = 1;
  466. }
  467. }
  468. end = strchr(p, DEFAULT_DIR_SEPARATOR);
  469. if (end) {
  470. if (filename_length > (MAXPATHLEN - 2) || (end-ptr) > MAXPATHLEN || (end-ptr) + 1 + filename_length + 1 >= MAXPATHLEN) {
  471. ptr = end + 1;
  472. continue;
  473. }
  474. memcpy(trypath, ptr, end-ptr);
  475. trypath[end-ptr] = '/';
  476. memcpy(trypath+(end-ptr)+1, filename, filename_length+1);
  477. ptr = end+1;
  478. } else {
  479. size_t len = strlen(ptr);
  480. if (filename_length > (MAXPATHLEN - 2) || len > MAXPATHLEN || len + 1 + filename_length + 1 >= MAXPATHLEN) {
  481. break;
  482. }
  483. memcpy(trypath, ptr, len);
  484. trypath[len] = '/';
  485. memcpy(trypath+len+1, filename, filename_length+1);
  486. ptr = NULL;
  487. }
  488. actual_path = trypath;
  489. if (is_stream_wrapper) {
  490. wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE);
  491. if (!wrapper) {
  492. continue;
  493. } else if (wrapper != &php_plain_files_wrapper) {
  494. if (wrapper->wops->url_stat) {
  495. php_stream_statbuf ssb;
  496. if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL)) {
  497. return zend_string_init(trypath, strlen(trypath), 0);
  498. }
  499. if (EG(exception)) {
  500. return NULL;
  501. }
  502. }
  503. continue;
  504. }
  505. }
  506. if (tsrm_realpath(actual_path, resolved_path)) {
  507. return zend_string_init(resolved_path, strlen(resolved_path), 0);
  508. }
  509. } /* end provided path */
  510. /* check in calling scripts' current working directory as a fall back case
  511. */
  512. if (zend_is_executing() &&
  513. (exec_filename = zend_get_executed_filename_ex()) != NULL) {
  514. const char *exec_fname = ZSTR_VAL(exec_filename);
  515. size_t exec_fname_length = ZSTR_LEN(exec_filename);
  516. while ((--exec_fname_length < SIZE_MAX) && !IS_SLASH(exec_fname[exec_fname_length]));
  517. if (exec_fname_length > 0 &&
  518. filename_length < (MAXPATHLEN - 2) &&
  519. exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
  520. memcpy(trypath, exec_fname, exec_fname_length + 1);
  521. memcpy(trypath+exec_fname_length + 1, filename, filename_length+1);
  522. actual_path = trypath;
  523. /* Check for stream wrapper */
  524. for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
  525. if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) {
  526. wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE);
  527. if (!wrapper) {
  528. return NULL;
  529. } else if (wrapper != &php_plain_files_wrapper) {
  530. if (wrapper->wops->url_stat) {
  531. php_stream_statbuf ssb;
  532. if (SUCCESS == wrapper->wops->url_stat(wrapper, trypath, PHP_STREAM_URL_STAT_QUIET, &ssb, NULL)) {
  533. return zend_string_init(trypath, strlen(trypath), 0);
  534. }
  535. if (EG(exception)) {
  536. return NULL;
  537. }
  538. }
  539. return NULL;
  540. }
  541. }
  542. if (tsrm_realpath(actual_path, resolved_path)) {
  543. return zend_string_init(resolved_path, strlen(resolved_path), 0);
  544. }
  545. }
  546. }
  547. return NULL;
  548. }
  549. /* }}} */
  550. /* {{{ php_fopen_with_path
  551. * Tries to open a file with a PATH-style list of directories.
  552. * If the filename starts with "." or "/", the path is ignored.
  553. */
  554. PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, zend_string **opened_path)
  555. {
  556. char *pathbuf, *ptr, *end;
  557. char trypath[MAXPATHLEN];
  558. FILE *fp;
  559. size_t filename_length;
  560. zend_string *exec_filename;
  561. if (opened_path) {
  562. *opened_path = NULL;
  563. }
  564. if (!filename) {
  565. return NULL;
  566. }
  567. filename_length = strlen(filename);
  568. #ifndef PHP_WIN32
  569. (void) filename_length;
  570. #endif
  571. /* Relative path open */
  572. if ((*filename == '.')
  573. /* Absolute path open */
  574. || IS_ABSOLUTE_PATH(filename, filename_length)
  575. || (!path || !*path)
  576. ) {
  577. return php_fopen_and_set_opened_path(filename, mode, opened_path);
  578. }
  579. /* check in provided path */
  580. /* append the calling scripts' current working directory
  581. * as a fall back case
  582. */
  583. if (zend_is_executing() &&
  584. (exec_filename = zend_get_executed_filename_ex()) != NULL) {
  585. const char *exec_fname = ZSTR_VAL(exec_filename);
  586. size_t exec_fname_length = ZSTR_LEN(exec_filename);
  587. while ((--exec_fname_length < SIZE_MAX) && !IS_SLASH(exec_fname[exec_fname_length]));
  588. if ((exec_fname && exec_fname[0] == '[') || exec_fname_length <= 0) {
  589. /* [no active file] or no path */
  590. pathbuf = estrdup(path);
  591. } else {
  592. size_t path_length = strlen(path);
  593. pathbuf = (char *) emalloc(exec_fname_length + path_length + 1 + 1);
  594. memcpy(pathbuf, path, path_length);
  595. pathbuf[path_length] = DEFAULT_DIR_SEPARATOR;
  596. memcpy(pathbuf + path_length + 1, exec_fname, exec_fname_length);
  597. pathbuf[path_length + exec_fname_length + 1] = '\0';
  598. }
  599. } else {
  600. pathbuf = estrdup(path);
  601. }
  602. ptr = pathbuf;
  603. while (ptr && *ptr) {
  604. end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
  605. if (end != NULL) {
  606. *end = '\0';
  607. end++;
  608. }
  609. if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) {
  610. php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN);
  611. }
  612. fp = php_fopen_and_set_opened_path(trypath, mode, opened_path);
  613. if (fp) {
  614. efree(pathbuf);
  615. return fp;
  616. }
  617. ptr = end;
  618. } /* end provided path */
  619. efree(pathbuf);
  620. return NULL;
  621. }
  622. /* }}} */
  623. /* {{{ php_strip_url_passwd
  624. */
  625. PHPAPI char *php_strip_url_passwd(char *url)
  626. {
  627. register char *p, *url_start;
  628. if (url == NULL) {
  629. return "";
  630. }
  631. p = url;
  632. while (*p) {
  633. if (*p == ':' && *(p + 1) == '/' && *(p + 2) == '/') {
  634. /* found protocol */
  635. url_start = p = p + 3;
  636. while (*p) {
  637. if (*p == '@') {
  638. int i;
  639. for (i = 0; i < 3 && url_start < p; i++, url_start++) {
  640. *url_start = '.';
  641. }
  642. for (; *p; p++) {
  643. *url_start++ = *p;
  644. }
  645. *url_start=0;
  646. break;
  647. }
  648. p++;
  649. }
  650. return url;
  651. }
  652. p++;
  653. }
  654. return url;
  655. }
  656. /* }}} */
  657. /* {{{ expand_filepath
  658. */
  659. PHPAPI char *expand_filepath(const char *filepath, char *real_path)
  660. {
  661. return expand_filepath_ex(filepath, real_path, NULL, 0);
  662. }
  663. /* }}} */
  664. /* {{{ expand_filepath_ex
  665. */
  666. PHPAPI char *expand_filepath_ex(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len)
  667. {
  668. return expand_filepath_with_mode(filepath, real_path, relative_to, relative_to_len, CWD_FILEPATH);
  669. }
  670. /* }}} */
  671. /* {{{ expand_filepath_use_realpath
  672. */
  673. PHPAPI char *expand_filepath_with_mode(const char *filepath, char *real_path, const char *relative_to, size_t relative_to_len, int realpath_mode)
  674. {
  675. cwd_state new_state;
  676. char cwd[MAXPATHLEN];
  677. size_t copy_len;
  678. size_t path_len;
  679. if (!filepath[0]) {
  680. return NULL;
  681. }
  682. path_len = strlen(filepath);
  683. if (IS_ABSOLUTE_PATH(filepath, path_len)) {
  684. cwd[0] = '\0';
  685. } else {
  686. const char *iam = SG(request_info).path_translated;
  687. const char *result;
  688. if (relative_to) {
  689. if (relative_to_len > MAXPATHLEN-1U) {
  690. return NULL;
  691. }
  692. result = relative_to;
  693. memcpy(cwd, relative_to, relative_to_len+1U);
  694. } else {
  695. result = VCWD_GETCWD(cwd, MAXPATHLEN);
  696. }
  697. if (!result && (iam != filepath)) {
  698. int fdtest = -1;
  699. fdtest = VCWD_OPEN(filepath, O_RDONLY);
  700. if (fdtest != -1) {
  701. /* return a relative file path if for any reason
  702. * we cannot cannot getcwd() and the requested,
  703. * relatively referenced file is accessible */
  704. copy_len = path_len > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : path_len;
  705. if (real_path) {
  706. memcpy(real_path, filepath, copy_len);
  707. real_path[copy_len] = '\0';
  708. } else {
  709. real_path = estrndup(filepath, copy_len);
  710. }
  711. close(fdtest);
  712. return real_path;
  713. } else {
  714. cwd[0] = '\0';
  715. }
  716. } else if (!result) {
  717. cwd[0] = '\0';
  718. }
  719. }
  720. new_state.cwd = estrdup(cwd);
  721. new_state.cwd_length = strlen(cwd);
  722. if (virtual_file_ex(&new_state, filepath, NULL, realpath_mode)) {
  723. efree(new_state.cwd);
  724. return NULL;
  725. }
  726. if (real_path) {
  727. copy_len = new_state.cwd_length > MAXPATHLEN - 1 ? MAXPATHLEN - 1 : new_state.cwd_length;
  728. memcpy(real_path, new_state.cwd, copy_len);
  729. real_path[copy_len] = '\0';
  730. } else {
  731. real_path = estrndup(new_state.cwd, new_state.cwd_length);
  732. }
  733. efree(new_state.cwd);
  734. return real_path;
  735. }
  736. /* }}} */