xref: /PHP-7.2/main/php_open_temporary_file.c (revision 281e2f82)
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