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