PageRenderTime 45ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/session/mod_files.c

http://github.com/php/php-src
C | 712 lines | 420 code | 111 blank | 181 comment | 96 complexity | 8b421c5742eab2ca2c69f7fbc92b5f76 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. | Author: Sascha Schumann <sascha@schumann.cx> |
  14. +----------------------------------------------------------------------+
  15. */
  16. /**************************************************************************
  17. * Files save handler should be used as reference implementations of session
  18. * save handlers. PS_* functions are called as follows with standard usage.
  19. *
  20. * PS_OPEN_FUNC() - Create module data that manages save handler.
  21. * PS_CREATE_SID() and/or PS_VALIDATE_SID()
  22. * - PS_CREATE_ID() is called if session ID(key) is not
  23. * provided or invalid. PS_VALIDATE_SID() is called to
  24. * verify session ID already exists or not to mitigate
  25. * session adoption vulnerability risk.
  26. * PS_READ_FUNC() - Read data from storage.
  27. * PS_GC_FUNC() - Perform GC. Called by probability
  28. * (session.gc_probability/session.gc_divisor).
  29. * PS_WRITE_FUNC() or PS_UPDATE_TIMESTAMP()
  30. * - Write session data or update session data timestamp.
  31. * It depends on session data change.
  32. * PS_CLOSE_FUNC() - Clean up module data created by PS_OPEN_FUNC().
  33. *
  34. * Session module guarantees PS_OPEN_FUNC() is called before calling other
  35. * PS_*_FUNC() functions. Other than this, session module may call any
  36. * PS_*_FUNC() at any time. You may assume non null *mod_data created by
  37. * PS_OPEN_FUNC() is passed to PS_*_FUNC().
  38. *
  39. * NOTE:
  40. * - Save handlers _MUST_NOT_ change/refer PS() values.
  41. * i.e. PS(id), PS(session_status), PS(mod) and any other PS() values.
  42. * Use only function parameters passed from session module.
  43. * - Save handler _MUST_ use PS_GET_MOD_DATA()/PS_SET_MOD_DATA() macro to
  44. * set/get save handler module data(mod_data). mod_data contains
  45. * data required by PS modules. It will not be NULL except PS_OPEN_FUNC().
  46. * - Refer to PS_* macros in php_session.h for function/parameter definitions.
  47. * - Returning FAILURE state from PS_* function results in raising errors.
  48. * Avoid failure state as much as possible.
  49. * - Use static ps_[module name]_[function name] functions for internal use.
  50. *************************************************************************/
  51. #include "php.h"
  52. #include <sys/stat.h>
  53. #include <sys/types.h>
  54. #if HAVE_SYS_FILE_H
  55. #include <sys/file.h>
  56. #endif
  57. #if HAVE_DIRENT_H
  58. #include <dirent.h>
  59. #endif
  60. #ifdef PHP_WIN32
  61. #include "win32/readdir.h"
  62. #endif
  63. #include <time.h>
  64. #include <fcntl.h>
  65. #include <errno.h>
  66. #if HAVE_UNISTD_H
  67. #include <unistd.h>
  68. #endif
  69. #include "php_session.h"
  70. #include "mod_files.h"
  71. #include "ext/standard/flock_compat.h"
  72. #include "php_open_temporary_file.h"
  73. #define FILE_PREFIX "sess_"
  74. #ifdef PHP_WIN32
  75. # ifndef O_NOFOLLOW
  76. # define O_NOFOLLOW 0
  77. # endif
  78. #endif
  79. typedef struct {
  80. char *lastkey;
  81. char *basedir;
  82. size_t basedir_len;
  83. size_t dirdepth;
  84. size_t st_size;
  85. int filemode;
  86. int fd;
  87. } ps_files;
  88. const ps_module ps_mod_files = {
  89. /* New save handlers MUST use PS_MOD_UPDATE_TIMESTAMP macro */
  90. PS_MOD_UPDATE_TIMESTAMP(files)
  91. };
  92. static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key)
  93. {
  94. size_t key_len;
  95. const char *p;
  96. int i;
  97. size_t n;
  98. key_len = strlen(key);
  99. if (!data || key_len <= data->dirdepth ||
  100. buflen < (strlen(data->basedir) + 2 * data->dirdepth + key_len + 5 + sizeof(FILE_PREFIX))) {
  101. return NULL;
  102. }
  103. p = key;
  104. memcpy(buf, data->basedir, data->basedir_len);
  105. n = data->basedir_len;
  106. buf[n++] = PHP_DIR_SEPARATOR;
  107. for (i = 0; i < (int)data->dirdepth; i++) {
  108. buf[n++] = *p++;
  109. buf[n++] = PHP_DIR_SEPARATOR;
  110. }
  111. memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
  112. n += sizeof(FILE_PREFIX) - 1;
  113. memcpy(buf + n, key, key_len);
  114. n += key_len;
  115. buf[n] = '\0';
  116. return buf;
  117. }
  118. #ifndef O_BINARY
  119. # define O_BINARY 0
  120. #endif
  121. static void ps_files_close(ps_files *data)
  122. {
  123. if (data->fd != -1) {
  124. #ifdef PHP_WIN32
  125. /* On Win32 locked files that are closed without being explicitly unlocked
  126. will be unlocked only when "system resources become available". */
  127. flock(data->fd, LOCK_UN);
  128. #endif
  129. close(data->fd);
  130. data->fd = -1;
  131. }
  132. }
  133. static void ps_files_open(ps_files *data, const char *key)
  134. {
  135. char buf[MAXPATHLEN];
  136. #if !defined(O_NOFOLLOW) || !defined(PHP_WIN32)
  137. struct stat sbuf;
  138. #endif
  139. int ret;
  140. if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
  141. if (data->lastkey) {
  142. efree(data->lastkey);
  143. data->lastkey = NULL;
  144. }
  145. ps_files_close(data);
  146. if (php_session_valid_key(key) == FAILURE) {
  147. php_error_docref(NULL, E_WARNING, "The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'");
  148. return;
  149. }
  150. if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
  151. php_error_docref(NULL, E_WARNING, "Failed to create session data file path. Too short session ID, invalid save_path or path lentgth exceeds MAXPATHLEN(%d)", MAXPATHLEN);
  152. return;
  153. }
  154. data->lastkey = estrdup(key);
  155. /* O_NOFOLLOW to prevent us from following evil symlinks */
  156. #ifdef O_NOFOLLOW
  157. data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY | O_NOFOLLOW, data->filemode);
  158. #else
  159. /* Check to make sure that the opened file is not outside of allowable dirs.
  160. This is not 100% safe but it's hard to do something better without O_NOFOLLOW */
  161. if(PG(open_basedir) && lstat(buf, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf)) {
  162. return;
  163. }
  164. data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode);
  165. #endif
  166. if (data->fd != -1) {
  167. #ifndef PHP_WIN32
  168. /* check that this session file was created by us or root – we
  169. don't want to end up accepting the sessions of another webapp
  170. If the process is ran by root, we ignore session file ownership
  171. Use case: session is initiated by Apache under non-root and then
  172. accessed by backend with root permissions to execute some system tasks.
  173. */
  174. if (zend_fstat(data->fd, &sbuf) || (sbuf.st_uid != 0 && sbuf.st_uid != getuid() && sbuf.st_uid != geteuid() && getuid() != 0)) {
  175. close(data->fd);
  176. data->fd = -1;
  177. php_error_docref(NULL, E_WARNING, "Session data file is not created by your uid");
  178. return;
  179. }
  180. #endif
  181. do {
  182. ret = flock(data->fd, LOCK_EX);
  183. } while (ret == -1 && errno == EINTR);
  184. #ifdef F_SETFD
  185. # ifndef FD_CLOEXEC
  186. # define FD_CLOEXEC 1
  187. # endif
  188. if (fcntl(data->fd, F_SETFD, FD_CLOEXEC)) {
  189. php_error_docref(NULL, E_WARNING, "fcntl(%d, F_SETFD, FD_CLOEXEC) failed: %s (%d)", data->fd, strerror(errno), errno);
  190. }
  191. #endif
  192. } else {
  193. php_error_docref(NULL, E_WARNING, "open(%s, O_RDWR) failed: %s (%d)", buf, strerror(errno), errno);
  194. }
  195. }
  196. }
  197. static int ps_files_write(ps_files *data, zend_string *key, zend_string *val)
  198. {
  199. size_t n = 0;
  200. /* PS(id) may be changed by calling session_regenerate_id().
  201. Re-initialization should be tried here. ps_files_open() checks
  202. data->lastkey and reopen when it is needed. */
  203. ps_files_open(data, ZSTR_VAL(key));
  204. if (data->fd < 0) {
  205. return FAILURE;
  206. }
  207. /* Truncate file if the amount of new data is smaller than the existing data set. */
  208. if (ZSTR_LEN(val) < data->st_size) {
  209. php_ignore_value(ftruncate(data->fd, 0));
  210. }
  211. #if defined(HAVE_PWRITE)
  212. n = pwrite(data->fd, ZSTR_VAL(val), ZSTR_LEN(val), 0);
  213. #else
  214. lseek(data->fd, 0, SEEK_SET);
  215. #ifdef PHP_WIN32
  216. {
  217. unsigned int to_write = ZSTR_LEN(val) > UINT_MAX ? UINT_MAX : (unsigned int)ZSTR_LEN(val);
  218. char *buf = ZSTR_VAL(val);
  219. int wrote;
  220. do {
  221. wrote = _write(data->fd, buf, to_write);
  222. n += wrote;
  223. buf = wrote > -1 ? buf + wrote : 0;
  224. to_write = wrote > -1 ? (ZSTR_LEN(val) - n > UINT_MAX ? UINT_MAX : (unsigned int)(ZSTR_LEN(val) - n)): 0;
  225. } while(wrote > 0);
  226. }
  227. #else
  228. n = write(data->fd, ZSTR_VAL(val), ZSTR_LEN(val));
  229. #endif
  230. #endif
  231. if (n != ZSTR_LEN(val)) {
  232. if (n == (size_t)-1) {
  233. php_error_docref(NULL, E_WARNING, "Write failed: %s (%d)", strerror(errno), errno);
  234. } else {
  235. php_error_docref(NULL, E_WARNING, "Write wrote less bytes than requested");
  236. }
  237. return FAILURE;
  238. }
  239. return SUCCESS;
  240. }
  241. static int ps_files_cleanup_dir(const char *dirname, zend_long maxlifetime)
  242. {
  243. DIR *dir;
  244. struct dirent *entry;
  245. zend_stat_t sbuf;
  246. char buf[MAXPATHLEN];
  247. time_t now;
  248. int nrdels = 0;
  249. size_t dirname_len;
  250. dir = opendir(dirname);
  251. if (!dir) {
  252. php_error_docref(NULL, E_NOTICE, "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)", dirname, strerror(errno), errno);
  253. return (0);
  254. }
  255. time(&now);
  256. dirname_len = strlen(dirname);
  257. if (dirname_len >= MAXPATHLEN) {
  258. php_error_docref(NULL, E_NOTICE, "ps_files_cleanup_dir: dirname(%s) is too long", dirname);
  259. closedir(dir);
  260. return (0);
  261. }
  262. /* Prepare buffer (dirname never changes) */
  263. memcpy(buf, dirname, dirname_len);
  264. buf[dirname_len] = PHP_DIR_SEPARATOR;
  265. while ((entry = readdir(dir))) {
  266. /* does the file start with our prefix? */
  267. if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
  268. size_t entry_len = strlen(entry->d_name);
  269. /* does it fit into our buffer? */
  270. if (entry_len + dirname_len + 2 < MAXPATHLEN) {
  271. /* create the full path.. */
  272. memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
  273. /* NUL terminate it and */
  274. buf[dirname_len + entry_len + 1] = '\0';
  275. /* check whether its last access was more than maxlifetime ago */
  276. if (VCWD_STAT(buf, &sbuf) == 0 &&
  277. (now - sbuf.st_mtime) > maxlifetime) {
  278. VCWD_UNLINK(buf);
  279. nrdels++;
  280. }
  281. }
  282. }
  283. }
  284. closedir(dir);
  285. return (nrdels);
  286. }
  287. static int ps_files_key_exists(ps_files *data, const char *key)
  288. {
  289. char buf[MAXPATHLEN];
  290. zend_stat_t sbuf;
  291. if (!key || !ps_files_path_create(buf, sizeof(buf), data, key)) {
  292. return FAILURE;
  293. }
  294. if (VCWD_STAT(buf, &sbuf)) {
  295. return FAILURE;
  296. }
  297. return SUCCESS;
  298. }
  299. #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
  300. /*
  301. * Open save handler. Setup resources that are needed by the handler.
  302. * PARAMETERS: PS_OPEN_ARGS in php_session.h
  303. * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL valid module data
  304. * (void **mod_data) with SUCCESS, NULL(default) for FAILUREs.
  305. *
  306. * Files save handler checks/create save_path directory and setup ps_files data.
  307. * Note that files save handler supports splitting session data into multiple
  308. * directories.
  309. * *mod_data, *save_path, *session_name are guaranteed to have non-NULL values.
  310. */
  311. PS_OPEN_FUNC(files)
  312. {
  313. ps_files *data;
  314. const char *p, *last;
  315. const char *argv[3];
  316. int argc = 0;
  317. size_t dirdepth = 0;
  318. int filemode = 0600;
  319. if (*save_path == '\0') {
  320. /* if save path is an empty string, determine the temporary dir */
  321. save_path = php_get_temporary_directory();
  322. if (php_check_open_basedir(save_path)) {
  323. return FAILURE;
  324. }
  325. }
  326. /* split up input parameter */
  327. last = save_path;
  328. p = strchr(save_path, ';');
  329. while (p) {
  330. argv[argc++] = last;
  331. last = ++p;
  332. p = strchr(p, ';');
  333. if (argc > 1) break;
  334. }
  335. argv[argc++] = last;
  336. if (argc > 1) {
  337. errno = 0;
  338. dirdepth = (size_t) ZEND_STRTOL(argv[0], NULL, 10);
  339. if (errno == ERANGE) {
  340. php_error(E_WARNING, "The first parameter in session.save_path is invalid");
  341. return FAILURE;
  342. }
  343. }
  344. if (argc > 2) {
  345. errno = 0;
  346. filemode = (int)ZEND_STRTOL(argv[1], NULL, 8);
  347. if (errno == ERANGE || filemode < 0 || filemode > 07777) {
  348. php_error(E_WARNING, "The second parameter in session.save_path is invalid");
  349. return FAILURE;
  350. }
  351. }
  352. save_path = argv[argc - 1];
  353. data = ecalloc(1, sizeof(*data));
  354. data->fd = -1;
  355. data->dirdepth = dirdepth;
  356. data->filemode = filemode;
  357. data->basedir_len = strlen(save_path);
  358. data->basedir = estrndup(save_path, data->basedir_len);
  359. if (PS_GET_MOD_DATA()) {
  360. ps_close_files(mod_data);
  361. }
  362. PS_SET_MOD_DATA(data);
  363. return SUCCESS;
  364. }
  365. /*
  366. * Clean up opened resources.
  367. * PARAMETERS: PS_CLOSE_ARGS in php_session.h
  368. * RETURN VALUE: SUCCESS. Must set PS module data(void **mod_data) to NULL.
  369. *
  370. * Files save handler closes open files and it's memory.
  371. * *mod_data is guaranteed to have non-NULL value.
  372. * PS_CLOSE_FUNC() must set *mod_data to NULL. PS_CLOSE_FUNC() should not
  373. * fail.
  374. */
  375. PS_CLOSE_FUNC(files)
  376. {
  377. PS_FILES_DATA;
  378. ps_files_close(data);
  379. if (data->lastkey) {
  380. efree(data->lastkey);
  381. data->lastkey = NULL;
  382. }
  383. efree(data->basedir);
  384. efree(data);
  385. PS_SET_MOD_DATA(NULL);
  386. return SUCCESS;
  387. }
  388. /*
  389. * Read session data from opened resource.
  390. * PARAMETERS: PS_READ_ARGS in php_session.h
  391. * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL session data to (zend_string **val)
  392. * for SUCCESS. NULL(default) for FAILUREs.
  393. *
  394. * Files save handler supports splitting session data into multiple
  395. * directories.
  396. * *mod_data, *key are guaranteed to have non-NULL values.
  397. */
  398. PS_READ_FUNC(files)
  399. {
  400. zend_long n = 0;
  401. zend_stat_t sbuf;
  402. PS_FILES_DATA;
  403. ps_files_open(data, ZSTR_VAL(key));
  404. if (data->fd < 0) {
  405. return FAILURE;
  406. }
  407. if (zend_fstat(data->fd, &sbuf)) {
  408. return FAILURE;
  409. }
  410. data->st_size = sbuf.st_size;
  411. if (sbuf.st_size == 0) {
  412. *val = ZSTR_EMPTY_ALLOC();
  413. return SUCCESS;
  414. }
  415. *val = zend_string_alloc(sbuf.st_size, 0);
  416. #if defined(HAVE_PREAD)
  417. n = pread(data->fd, ZSTR_VAL(*val), ZSTR_LEN(*val), 0);
  418. #else
  419. lseek(data->fd, 0, SEEK_SET);
  420. #ifdef PHP_WIN32
  421. {
  422. unsigned int to_read = ZSTR_LEN(*val) > UINT_MAX ? UINT_MAX : (unsigned int)ZSTR_LEN(*val);
  423. char *buf = ZSTR_VAL(*val);
  424. int read_in;
  425. do {
  426. read_in = _read(data->fd, buf, to_read);
  427. n += read_in;
  428. buf = read_in > -1 ? buf + read_in : 0;
  429. to_read = read_in > -1 ? (ZSTR_LEN(*val) - n > UINT_MAX ? UINT_MAX : (unsigned int)(ZSTR_LEN(*val) - n)): 0;
  430. } while(read_in > 0);
  431. }
  432. #else
  433. n = read(data->fd, ZSTR_VAL(*val), ZSTR_LEN(*val));
  434. #endif
  435. #endif
  436. if (n != (zend_long)sbuf.st_size) {
  437. if (n == -1) {
  438. php_error_docref(NULL, E_WARNING, "Read failed: %s (%d)", strerror(errno), errno);
  439. } else {
  440. php_error_docref(NULL, E_WARNING, "Read returned less bytes than requested");
  441. }
  442. zend_string_release_ex(*val, 0);
  443. *val = ZSTR_EMPTY_ALLOC();
  444. return FAILURE;
  445. }
  446. ZSTR_VAL(*val)[ZSTR_LEN(*val)] = '\0';
  447. return SUCCESS;
  448. }
  449. /*
  450. * Write session data.
  451. * PARAMETERS: PS_WRITE_ARGS in php_session.h
  452. * RETURN VALUE: SUCCESS or FAILURE.
  453. *
  454. * PS_WRITE_FUNC() must write session data(zend_string *val) unconditionally.
  455. * *mod_data, *key, *val are guaranteed to have non-NULL values.
  456. */
  457. PS_WRITE_FUNC(files)
  458. {
  459. PS_FILES_DATA;
  460. return ps_files_write(data, key, val);
  461. }
  462. /*
  463. * Update session data modification/access time stamp.
  464. * PARAMETERS: PS_UPDATE_TIMESTAMP_ARGS in php_session.h
  465. * RETURN VALUE: SUCCESS or FAILURE.
  466. *
  467. * PS_UPDATE_TIMESTAMP_FUNC() updates time stamp(mtime) so that active session
  468. * data files will not be purged by GC. If session data storage does not need to
  469. * update timestamp, it should return SUCCESS simply. (e.g. Memcache)
  470. * *mod_data, *key, *val are guaranteed to have non-NULL values.
  471. *
  472. * NOTE: Updating access timestamp at PS_READ_FUNC() may extend life of obsolete
  473. * session data. Use of PS_UPDATE_TIMESTAMP_FUNC() is preferred whenever it is
  474. * possible.
  475. */
  476. PS_UPDATE_TIMESTAMP_FUNC(files)
  477. {
  478. char buf[MAXPATHLEN];
  479. int ret;
  480. PS_FILES_DATA;
  481. if (!ps_files_path_create(buf, sizeof(buf), data, ZSTR_VAL(key))) {
  482. return FAILURE;
  483. }
  484. /* Update mtime */
  485. ret = VCWD_UTIME(buf, NULL);
  486. if (ret == -1) {
  487. /* New session ID, create data file */
  488. return ps_files_write(data, key, val);
  489. }
  490. return SUCCESS;
  491. }
  492. /*
  493. * Delete session data.
  494. * PARAMETERS: PS_DESTROY_ARGS in php_session.h
  495. * RETURN VALUE: SUCCESS or FAILURE.
  496. *
  497. * PS_DESTROY_FUNC() must remove the session data specified by *key from
  498. * session data storage unconditionally. It must not return FAILURE for
  499. * non-existent session data.
  500. * *mod_data, *key are guaranteed to have non-NULL values.
  501. */
  502. PS_DESTROY_FUNC(files)
  503. {
  504. char buf[MAXPATHLEN];
  505. PS_FILES_DATA;
  506. if (!ps_files_path_create(buf, sizeof(buf), data, ZSTR_VAL(key))) {
  507. return FAILURE;
  508. }
  509. if (data->fd != -1) {
  510. ps_files_close(data);
  511. if (VCWD_UNLINK(buf) == -1) {
  512. /* This is a little safety check for instances when we are dealing with a regenerated session
  513. * that was not yet written to disk. */
  514. if (!VCWD_ACCESS(buf, F_OK)) {
  515. return FAILURE;
  516. }
  517. }
  518. }
  519. return SUCCESS;
  520. }
  521. /*
  522. * Cleanup expired session data.
  523. * PARAMETERS: PS_GC_ARGS in php_session.h
  524. * RETURN VALUE: SUCCESS or FAILURE. Number of deleted records(int *nrdels(default=-1)).
  525. *
  526. * PS_GC_FUNC() must remove session data that are not accessed
  527. * 'session.maxlifetime'(seconds). If storage does not need manual GC, it
  528. * may return SUCCESS simply. (e.g. Memcache) It must set number of records
  529. * deleted(nrdels).
  530. * *mod_data is guaranteed to have non-NULL value.
  531. */
  532. PS_GC_FUNC(files)
  533. {
  534. PS_FILES_DATA;
  535. /* We don't perform any cleanup, if dirdepth is larger than 0.
  536. we return SUCCESS, since all cleanup should be handled by
  537. an external entity (i.e. find -ctime x | xargs rm) */
  538. if (data->dirdepth == 0) {
  539. *nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime);
  540. } else {
  541. *nrdels = -1; // Cannot process multiple depth save dir
  542. }
  543. return *nrdels;
  544. }
  545. /*
  546. * Create session ID.
  547. * PARAMETERS: PS_CREATE_SID_ARGS in php_session.h
  548. * RETURN VALUE: Valid session ID(zend_string *) or NULL for FAILURE.
  549. *
  550. * PS_CREATE_SID_FUNC() must check collision. i.e. Check session data if
  551. * new sid exists already.
  552. * *mod_data is guaranteed to have non-NULL value.
  553. * NOTE: Default php_session_create_id() does not check collision. If
  554. * NULL is returned, session module create new ID by using php_session_create_id().
  555. * If php_session_create_id() fails due to invalid configuration, it raises E_ERROR.
  556. * NULL return value checks from php_session_create_id() is not required generally.
  557. */
  558. PS_CREATE_SID_FUNC(files)
  559. {
  560. zend_string *sid;
  561. int maxfail = 3;
  562. PS_FILES_DATA;
  563. do {
  564. sid = php_session_create_id((void**)&data);
  565. if (!sid) {
  566. if (--maxfail < 0) {
  567. return NULL;
  568. } else {
  569. continue;
  570. }
  571. }
  572. /* Check collision */
  573. /* FIXME: mod_data(data) should not be NULL (User handler could be NULL) */
  574. if (data && ps_files_key_exists(data, ZSTR_VAL(sid)) == SUCCESS) {
  575. if (sid) {
  576. zend_string_release_ex(sid, 0);
  577. sid = NULL;
  578. }
  579. if (--maxfail < 0) {
  580. return NULL;
  581. }
  582. }
  583. } while(!sid);
  584. return sid;
  585. }
  586. /*
  587. * Check session ID existence for use_strict_mode support.
  588. * PARAMETERS: PS_VALIDATE_SID_ARGS in php_session.h
  589. * RETURN VALUE: SUCCESS or FAILURE.
  590. *
  591. * Return SUCCESS for valid key(already existing session).
  592. * Return FAILURE for invalid key(non-existing session).
  593. * *mod_data, *key are guaranteed to have non-NULL values.
  594. */
  595. PS_VALIDATE_SID_FUNC(files)
  596. {
  597. PS_FILES_DATA;
  598. return ps_files_key_exists(data, ZSTR_VAL(key));
  599. }