fopen_wrappers.c 23 KB

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