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