1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 | Author: Zeev Suraski <zeev@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "php_open_temporary_file.h"
21
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26
27 #ifdef PHP_WIN32
28 #define O_RDONLY _O_RDONLY
29 #include "win32/param.h"
30 #include "win32/winutil.h"
31 #else
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #if HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
38 #endif
39 #endif
40 #ifdef HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #endif
43
44 #ifdef HAVE_SYS_FILE_H
45 #include <sys/file.h>
46 #endif
47
48 #if !defined(P_tmpdir)
49 #define P_tmpdir ""
50 #endif
51
52 /* {{{ php_do_open_temporary_file */
53
54 /* Loosely based on a tempnam() implementation by UCLA */
55
56 /*
57 * Copyright (c) 1988, 1993
58 * The Regents of the University of California. All rights reserved.
59 *
60 * Redistribution and use in source and binary forms, with or without
61 * modification, are permitted provided that the following conditions
62 * are met:
63 * 1. Redistributions of source code must retain the above copyright
64 * notice, this list of conditions and the following disclaimer.
65 * 2. Redistributions in binary form must reproduce the above copyright
66 * notice, this list of conditions and the following disclaimer in the
67 * documentation and/or other materials provided with the distribution.
68 * 3. All advertising materials mentioning features or use of this software
69 * must display the following acknowledgement:
70 * This product includes software developed by the University of
71 * California, Berkeley and its contributors.
72 * 4. Neither the name of the University nor the names of its contributors
73 * may be used to endorse or promote products derived from this software
74 * without specific prior written permission.
75 *
76 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
77 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
78 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
79 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
80 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
81 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
82 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
83 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
84 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
85 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
86 * SUCH DAMAGE.
87 */
88
php_do_open_temporary_file(const char * path,const char * pfx,zend_string ** opened_path_p)89 static int php_do_open_temporary_file(const char *path, const char *pfx, zend_string **opened_path_p)
90 {
91 #ifdef PHP_WIN32
92 char *opened_path = NULL;
93 size_t opened_path_len;
94 wchar_t *cwdw, *pfxw, pathw[MAXPATHLEN];
95 #else
96 char opened_path[MAXPATHLEN];
97 char *trailing_slash;
98 #endif
99 char cwd[MAXPATHLEN];
100 cwd_state new_state;
101 int fd = -1;
102 #ifndef HAVE_MKSTEMP
103 int open_flags = O_CREAT | O_TRUNC | O_RDWR
104 #ifdef PHP_WIN32
105 | _O_BINARY
106 #endif
107 ;
108 #endif
109
110 if (!path || !path[0]) {
111 return -1;
112 }
113
114 #ifdef PHP_WIN32
115 if (!php_win32_check_trailing_space(pfx, strlen(pfx))) {
116 SetLastError(ERROR_INVALID_NAME);
117 return -1;
118 }
119 #endif
120
121 if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
122 cwd[0] = '\0';
123 }
124
125 new_state.cwd = estrdup(cwd);
126 new_state.cwd_length = strlen(cwd);
127
128 if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
129 efree(new_state.cwd);
130 return -1;
131 }
132
133 #ifndef PHP_WIN32
134 if (IS_SLASH(new_state.cwd[new_state.cwd_length - 1])) {
135 trailing_slash = "";
136 } else {
137 trailing_slash = "/";
138 }
139
140 if (snprintf(opened_path, MAXPATHLEN, "%s%s%sXXXXXX", new_state.cwd, trailing_slash, pfx) >= MAXPATHLEN) {
141 efree(new_state.cwd);
142 return -1;
143 }
144 #endif
145
146 #ifdef PHP_WIN32
147 cwdw = php_win32_ioutil_any_to_w(new_state.cwd);
148 pfxw = php_win32_ioutil_any_to_w(pfx);
149 if (!cwdw || !pfxw) {
150 free(cwdw);
151 free(pfxw);
152 efree(new_state.cwd);
153 return -1;
154 }
155
156 if (GetTempFileNameW(cwdw, pfxw, 0, pathw)) {
157 opened_path = php_win32_ioutil_conv_w_to_any(pathw, PHP_WIN32_CP_IGNORE_LEN, &opened_path_len);
158 if (!opened_path || opened_path_len >= MAXPATHLEN) {
159 free(cwdw);
160 free(pfxw);
161 efree(new_state.cwd);
162 return -1;
163 }
164 assert(strlen(opened_path) == opened_path_len);
165
166 /* Some versions of windows set the temp file to be read-only,
167 * which means that opening it will fail... */
168 if (VCWD_CHMOD(opened_path, 0600)) {
169 free(cwdw);
170 free(pfxw);
171 efree(new_state.cwd);
172 free(opened_path);
173 return -1;
174 }
175 fd = VCWD_OPEN_MODE(opened_path, open_flags, 0600);
176 }
177
178 free(cwdw);
179 free(pfxw);
180 #elif defined(HAVE_MKSTEMP)
181 fd = mkstemp(opened_path);
182 #else
183 if (mktemp(opened_path)) {
184 fd = VCWD_OPEN(opened_path, open_flags);
185 }
186 #endif
187
188 #ifdef PHP_WIN32
189 if (fd != -1 && opened_path_p) {
190 *opened_path_p = zend_string_init(opened_path, opened_path_len, 0);
191 }
192 free(opened_path);
193 #else
194 if (fd != -1 && opened_path_p) {
195 *opened_path_p = zend_string_init(opened_path, strlen(opened_path), 0);
196 }
197 #endif
198 efree(new_state.cwd);
199 return fd;
200 }
201 /* }}} */
202
203 /*
204 * Determine where to place temporary files.
205 */
php_get_temporary_directory(void)206 PHPAPI const char* php_get_temporary_directory(void)
207 {
208 /* Did we determine the temporary directory already? */
209 if (PG(php_sys_temp_dir)) {
210 return PG(php_sys_temp_dir);
211 }
212
213 /* Is there a temporary directory "sys_temp_dir" in .ini defined? */
214 {
215 char *sys_temp_dir = PG(sys_temp_dir);
216 if (sys_temp_dir) {
217 size_t len = strlen(sys_temp_dir);
218 if (len >= 2 && sys_temp_dir[len - 1] == DEFAULT_SLASH) {
219 PG(php_sys_temp_dir) = estrndup(sys_temp_dir, len - 1);
220 return PG(php_sys_temp_dir);
221 } else if (len >= 1 && sys_temp_dir[len - 1] != DEFAULT_SLASH) {
222 PG(php_sys_temp_dir) = estrndup(sys_temp_dir, len);
223 return PG(php_sys_temp_dir);
224 }
225 }
226 }
227
228 #ifdef PHP_WIN32
229 /* We can't count on the environment variables TEMP or TMP,
230 * and so must make the Win32 API call to get the default
231 * directory for temporary files. Note this call checks
232 * the environment values TMP and TEMP (in order) first.
233 */
234 {
235 wchar_t sTemp[MAXPATHLEN];
236 char *tmp;
237 size_t len = GetTempPathW(MAXPATHLEN, sTemp);
238
239 if (!len) {
240 return NULL;
241 }
242
243 if (NULL == (tmp = php_win32_ioutil_conv_w_to_any(sTemp, len, &len))) {
244 return NULL;
245 }
246
247 PG(php_sys_temp_dir) = estrndup(tmp, len - 1);
248
249 free(tmp);
250 return PG(php_sys_temp_dir);
251 }
252 #else
253 /* On Unix use the (usual) TMPDIR environment variable. */
254 {
255 char* s = getenv("TMPDIR");
256 if (s && *s) {
257 size_t len = strlen(s);
258
259 if (s[len - 1] == DEFAULT_SLASH) {
260 PG(php_sys_temp_dir) = estrndup(s, len - 1);
261 } else {
262 PG(php_sys_temp_dir) = estrndup(s, len);
263 }
264
265 return PG(php_sys_temp_dir);
266 }
267 }
268 #ifdef P_tmpdir
269 /* Use the standard default temporary directory. */
270 if (P_tmpdir) {
271 PG(php_sys_temp_dir) = estrdup(P_tmpdir);
272 return PG(php_sys_temp_dir);
273 }
274 #endif
275 /* Shouldn't ever(!) end up here ... last ditch default. */
276 PG(php_sys_temp_dir) = estrdup("/tmp");
277 return PG(php_sys_temp_dir);
278 #endif
279 }
280
281 /* {{{ php_open_temporary_file
282 *
283 * Unlike tempnam(), the supplied dir argument takes precedence
284 * over the TMPDIR environment variable
285 * This function should do its best to return a file pointer to a newly created
286 * unique file, on every platform.
287 */
php_open_temporary_fd_ex(const char * dir,const char * pfx,zend_string ** opened_path_p,uint32_t flags)288 PHPAPI int php_open_temporary_fd_ex(const char *dir, const char *pfx, zend_string **opened_path_p, uint32_t flags)
289 {
290 int fd;
291 const char *temp_dir;
292
293 if (!pfx) {
294 pfx = "tmp.";
295 }
296 if (opened_path_p) {
297 *opened_path_p = NULL;
298 }
299
300 if (!dir || *dir == '\0') {
301 def_tmp:
302 temp_dir = php_get_temporary_directory();
303
304 if (temp_dir &&
305 *temp_dir != '\0' &&
306 (!(flags & PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ON_FALLBACK) || !php_check_open_basedir(temp_dir))) {
307 return php_do_open_temporary_file(temp_dir, pfx, opened_path_p);
308 } else {
309 return -1;
310 }
311 }
312
313 if ((flags & PHP_TMP_FILE_OPEN_BASEDIR_CHECK_ON_EXPLICIT_DIR) && php_check_open_basedir(dir)) {
314 return -1;
315 }
316
317 /* Try the directory given as parameter. */
318 fd = php_do_open_temporary_file(dir, pfx, opened_path_p);
319 if (fd == -1) {
320 /* Use default temporary directory. */
321 if (!(flags & PHP_TMP_FILE_SILENT)) {
322 php_error_docref(NULL, E_NOTICE, "file created in the system's temporary directory");
323 }
324 goto def_tmp;
325 }
326 return fd;
327 }
328
php_open_temporary_fd(const char * dir,const char * pfx,zend_string ** opened_path_p)329 PHPAPI int php_open_temporary_fd(const char *dir, const char *pfx, zend_string **opened_path_p)
330 {
331 return php_open_temporary_fd_ex(dir, pfx, opened_path_p, PHP_TMP_FILE_DEFAULT);
332 }
333
php_open_temporary_file(const char * dir,const char * pfx,zend_string ** opened_path_p)334 PHPAPI FILE *php_open_temporary_file(const char *dir, const char *pfx, zend_string **opened_path_p)
335 {
336 FILE *fp;
337 int fd = php_open_temporary_fd(dir, pfx, opened_path_p);
338
339 if (fd == -1) {
340 return NULL;
341 }
342
343 fp = fdopen(fd, "r+b");
344 if (fp == NULL) {
345 close(fd);
346 }
347
348 return fp;
349 }
350 /* }}} */
351