This source file includes following definitions.
- ps_files_path_create
- ps_files_close
- ps_files_open
- ps_files_cleanup_dir
- ps_files_key_exists
- PS_OPEN_FUNC
- PS_CLOSE_FUNC
- PS_READ_FUNC
- PS_WRITE_FUNC
- PS_DESTROY_FUNC
- PS_GC_FUNC
- PS_CREATE_SID_FUNC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 #include "php.h"
22
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #if HAVE_SYS_FILE_H
27 #include <sys/file.h>
28 #endif
29
30 #if HAVE_DIRENT_H
31 #include <dirent.h>
32 #endif
33
34 #ifdef PHP_WIN32
35 #include "win32/readdir.h"
36 #endif
37 #include <time.h>
38
39 #include <fcntl.h>
40 #include <errno.h>
41
42 #if HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #include "php_session.h"
47 #include "mod_files.h"
48 #include "ext/standard/flock_compat.h"
49 #include "php_open_temporary_file.h"
50
51 #define FILE_PREFIX "sess_"
52
53 #ifdef PHP_WIN32
54 # ifndef O_NOFOLLOW
55 # define O_NOFOLLOW 0
56 # endif
57 #endif
58
59 typedef struct {
60 int fd;
61 char *lastkey;
62 char *basedir;
63 size_t basedir_len;
64 size_t dirdepth;
65 size_t st_size;
66 int filemode;
67 } ps_files;
68
69 ps_module ps_mod_files = {
70 PS_MOD_SID(files)
71 };
72
73
74 static char *ps_files_path_create(char *buf, size_t buflen, ps_files *data, const char *key)
75 {
76 size_t key_len;
77 const char *p;
78 int i;
79 int n;
80
81 key_len = strlen(key);
82 if (key_len <= data->dirdepth ||
83 buflen < (strlen(data->basedir) + 2 * data->dirdepth + key_len + 5 + sizeof(FILE_PREFIX))) {
84 return NULL;
85 }
86
87 p = key;
88 memcpy(buf, data->basedir, data->basedir_len);
89 n = data->basedir_len;
90 buf[n++] = PHP_DIR_SEPARATOR;
91 for (i = 0; i < (int)data->dirdepth; i++) {
92 buf[n++] = *p++;
93 buf[n++] = PHP_DIR_SEPARATOR;
94 }
95 memcpy(buf + n, FILE_PREFIX, sizeof(FILE_PREFIX) - 1);
96 n += sizeof(FILE_PREFIX) - 1;
97 memcpy(buf + n, key, key_len);
98 n += key_len;
99 buf[n] = '\0';
100
101 return buf;
102 }
103
104 #ifndef O_BINARY
105 # define O_BINARY 0
106 #endif
107
108 static void ps_files_close(ps_files *data)
109 {
110 if (data->fd != -1) {
111 #ifdef PHP_WIN32
112
113
114 flock(data->fd, LOCK_UN);
115 #endif
116 close(data->fd);
117 data->fd = -1;
118 }
119 }
120
121 static void ps_files_open(ps_files *data, const char *key TSRMLS_DC)
122 {
123 char buf[MAXPATHLEN];
124 struct stat sbuf;
125 int ret;
126
127 if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) {
128 if (data->lastkey) {
129 efree(data->lastkey);
130 data->lastkey = NULL;
131 }
132
133 ps_files_close(data);
134
135 if (php_session_valid_key(key) == FAILURE) {
136 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,'");
137 return;
138 }
139
140 if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
141 return;
142 }
143
144 data->lastkey = estrdup(key);
145
146
147 #ifdef O_NOFOLLOW
148 data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY | O_NOFOLLOW, data->filemode);
149 #else
150
151
152 if(PG(open_basedir) && lstat(buf, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf TSRMLS_CC)) {
153 return;
154 }
155 data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode);
156 #endif
157
158 if (data->fd != -1) {
159 #ifndef PHP_WIN32
160
161
162 if (fstat(data->fd, &sbuf) || (sbuf.st_uid != 0 && sbuf.st_uid != getuid() && sbuf.st_uid != geteuid())) {
163 close(data->fd);
164 data->fd = -1;
165 return;
166 }
167 #endif
168 do {
169 ret = flock(data->fd, LOCK_EX);
170 } while (ret == -1 && errno == EINTR);
171
172 #ifdef F_SETFD
173 # ifndef FD_CLOEXEC
174 # define FD_CLOEXEC 1
175 # endif
176 if (fcntl(data->fd, F_SETFD, FD_CLOEXEC)) {
177 php_error_docref(NULL TSRMLS_CC, E_WARNING, "fcntl(%d, F_SETFD, FD_CLOEXEC) failed: %s (%d)", data->fd, strerror(errno), errno);
178 }
179 #endif
180 } else {
181 php_error_docref(NULL TSRMLS_CC, E_WARNING, "open(%s, O_RDWR) failed: %s (%d)", buf, strerror(errno), errno);
182 }
183 }
184 }
185
186 static int ps_files_cleanup_dir(const char *dirname, int maxlifetime TSRMLS_DC)
187 {
188 DIR *dir;
189 char dentry[sizeof(struct dirent) + MAXPATHLEN];
190 struct dirent *entry = (struct dirent *) &dentry;
191 struct stat sbuf;
192 char buf[MAXPATHLEN];
193 time_t now;
194 int nrdels = 0;
195 size_t dirname_len;
196
197 dir = opendir(dirname);
198 if (!dir) {
199 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ps_files_cleanup_dir: opendir(%s) failed: %s (%d)", dirname, strerror(errno), errno);
200 return (0);
201 }
202
203 time(&now);
204
205 dirname_len = strlen(dirname);
206
207
208 memcpy(buf, dirname, dirname_len);
209 buf[dirname_len] = PHP_DIR_SEPARATOR;
210
211 while (php_readdir_r(dir, (struct dirent *) dentry, &entry) == 0 && entry) {
212
213 if (!strncmp(entry->d_name, FILE_PREFIX, sizeof(FILE_PREFIX) - 1)) {
214 size_t entry_len = strlen(entry->d_name);
215
216
217 if (entry_len + dirname_len + 2 < MAXPATHLEN) {
218
219 memcpy(buf + dirname_len + 1, entry->d_name, entry_len);
220
221
222 buf[dirname_len + entry_len + 1] = '\0';
223
224
225 if (VCWD_STAT(buf, &sbuf) == 0 &&
226 (now - sbuf.st_mtime) > maxlifetime) {
227 VCWD_UNLINK(buf);
228 nrdels++;
229 }
230 }
231 }
232 }
233
234 closedir(dir);
235
236 return (nrdels);
237 }
238
239 static int ps_files_key_exists(ps_files *data, const char *key TSRMLS_DC)
240 {
241 char buf[MAXPATHLEN];
242 struct stat sbuf;
243
244 if (!key || !ps_files_path_create(buf, sizeof(buf), data, key)) {
245 return FAILURE;
246 }
247 if (VCWD_STAT(buf, &sbuf)) {
248 return FAILURE;
249 }
250 return SUCCESS;
251 }
252
253
254 #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
255
256 PS_OPEN_FUNC(files)
257 {
258 ps_files *data;
259 const char *p, *last;
260 const char *argv[3];
261 int argc = 0;
262 size_t dirdepth = 0;
263 int filemode = 0600;
264
265 if (*save_path == '\0') {
266
267 save_path = php_get_temporary_directory(TSRMLS_C);
268
269 if (php_check_open_basedir(save_path TSRMLS_CC)) {
270 return FAILURE;
271 }
272 }
273
274
275 last = save_path;
276 p = strchr(save_path, ';');
277 while (p) {
278 argv[argc++] = last;
279 last = ++p;
280 p = strchr(p, ';');
281 if (argc > 1) break;
282 }
283 argv[argc++] = last;
284
285 if (argc > 1) {
286 errno = 0;
287 dirdepth = (size_t) strtol(argv[0], NULL, 10);
288 if (errno == ERANGE) {
289 php_error(E_WARNING, "The first parameter in session.save_path is invalid");
290 return FAILURE;
291 }
292 }
293
294 if (argc > 2) {
295 errno = 0;
296 filemode = strtol(argv[1], NULL, 8);
297 if (errno == ERANGE || filemode < 0 || filemode > 07777) {
298 php_error(E_WARNING, "The second parameter in session.save_path is invalid");
299 return FAILURE;
300 }
301 }
302 save_path = argv[argc - 1];
303
304 data = ecalloc(1, sizeof(*data));
305
306 data->fd = -1;
307 data->dirdepth = dirdepth;
308 data->filemode = filemode;
309 data->basedir_len = strlen(save_path);
310 data->basedir = estrndup(save_path, data->basedir_len);
311
312 if (PS_GET_MOD_DATA()) {
313 ps_close_files(mod_data TSRMLS_CC);
314 }
315 PS_SET_MOD_DATA(data);
316
317 return SUCCESS;
318 }
319
320 PS_CLOSE_FUNC(files)
321 {
322 PS_FILES_DATA;
323
324 ps_files_close(data);
325
326 if (data->lastkey) {
327 efree(data->lastkey);
328 data->lastkey = NULL;
329 }
330
331 efree(data->basedir);
332 efree(data);
333 *mod_data = NULL;
334
335 return SUCCESS;
336 }
337
338 PS_READ_FUNC(files)
339 {
340 long n;
341 struct stat sbuf;
342 PS_FILES_DATA;
343
344
345 if (PS(use_strict_mode) &&
346 ps_files_key_exists(data, key TSRMLS_CC) == FAILURE) {
347
348 if (key) {
349 efree(PS(id));
350 PS(id) = NULL;
351 }
352 PS(id) = PS(mod)->s_create_sid((void **)&data, NULL TSRMLS_CC);
353 if (!PS(id)) {
354 return FAILURE;
355 }
356 if (PS(use_cookies)) {
357 PS(send_cookie) = 1;
358 }
359 php_session_reset_id(TSRMLS_C);
360 PS(session_status) = php_session_active;
361 }
362
363 if (!PS(id)) {
364 return FAILURE;
365 }
366
367 ps_files_open(data, PS(id) TSRMLS_CC);
368 if (data->fd < 0) {
369 return FAILURE;
370 }
371
372 if (fstat(data->fd, &sbuf)) {
373 return FAILURE;
374 }
375
376 data->st_size = *vallen = sbuf.st_size;
377
378 if (sbuf.st_size == 0) {
379 *val = STR_EMPTY_ALLOC();
380 return SUCCESS;
381 }
382
383 *val = emalloc(sbuf.st_size);
384
385 #if defined(HAVE_PREAD)
386 n = pread(data->fd, *val, sbuf.st_size, 0);
387 #else
388 lseek(data->fd, 0, SEEK_SET);
389 n = read(data->fd, *val, sbuf.st_size);
390 #endif
391
392 if (n != sbuf.st_size) {
393 if (n == -1) {
394 php_error_docref(NULL TSRMLS_CC, E_WARNING, "read failed: %s (%d)", strerror(errno), errno);
395 } else {
396 php_error_docref(NULL TSRMLS_CC, E_WARNING, "read returned less bytes than requested");
397 }
398 efree(*val);
399 return FAILURE;
400 }
401
402 return SUCCESS;
403 }
404
405 PS_WRITE_FUNC(files)
406 {
407 long n;
408 PS_FILES_DATA;
409
410 ps_files_open(data, key TSRMLS_CC);
411 if (data->fd < 0) {
412 return FAILURE;
413 }
414
415
416
417 if (vallen < (int)data->st_size) {
418 php_ignore_value(ftruncate(data->fd, 0));
419 }
420
421 #if defined(HAVE_PWRITE)
422 n = pwrite(data->fd, val, vallen, 0);
423 #else
424 lseek(data->fd, 0, SEEK_SET);
425 n = write(data->fd, val, vallen);
426 #endif
427
428 if (n != vallen) {
429 if (n == -1) {
430 php_error_docref(NULL TSRMLS_CC, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
431 } else {
432 php_error_docref(NULL TSRMLS_CC, E_WARNING, "write wrote less bytes than requested");
433 }
434 return FAILURE;
435 }
436
437 return SUCCESS;
438 }
439
440 PS_DESTROY_FUNC(files)
441 {
442 char buf[MAXPATHLEN];
443 PS_FILES_DATA;
444
445 if (!ps_files_path_create(buf, sizeof(buf), data, key)) {
446 return FAILURE;
447 }
448
449 if (data->fd != -1) {
450 ps_files_close(data);
451
452 if (VCWD_UNLINK(buf) == -1) {
453
454
455 if (!VCWD_ACCESS(buf, F_OK)) {
456 return FAILURE;
457 }
458 }
459 }
460
461 return SUCCESS;
462 }
463
464 PS_GC_FUNC(files)
465 {
466 PS_FILES_DATA;
467
468
469
470
471
472 if (data->dirdepth == 0) {
473 *nrdels = ps_files_cleanup_dir(data->basedir, maxlifetime TSRMLS_CC);
474 }
475
476 return SUCCESS;
477 }
478
479 PS_CREATE_SID_FUNC(files)
480 {
481 char *sid;
482 int maxfail = 3;
483 PS_FILES_DATA;
484
485 do {
486 sid = php_session_create_id((void **)&data, newlen TSRMLS_CC);
487
488 if (data && ps_files_key_exists(data, sid TSRMLS_CC) == SUCCESS) {
489 if (sid) {
490 efree(sid);
491 sid = NULL;
492 }
493 if (!(maxfail--)) {
494 return NULL;
495 }
496 }
497 } while(!sid);
498
499 return sid;
500 }
501
502
503
504
505
506
507
508
509
510