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