xref: /PHP-5.3/ext/standard/exec.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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: Rasmus Lerdorf <rasmus@php.net>                              |
16    |         Ilia Alshanetsky <iliaa@php.net>                             |
17    +----------------------------------------------------------------------+
18  */
19 /* $Id$ */
20 
21 #include <stdio.h>
22 #include "php.h"
23 #include <ctype.h>
24 #include "php_string.h"
25 #include "safe_mode.h"
26 #include "ext/standard/head.h"
27 #include "ext/standard/file.h"
28 #include "basic_functions.h"
29 #include "exec.h"
30 #include "php_globals.h"
31 #include "SAPI.h"
32 
33 #if HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif
36 #if HAVE_SIGNAL_H
37 #include <signal.h>
38 #endif
39 
40 #if HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43 #if HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 #if HAVE_FCNTL_H
47 #include <fcntl.h>
48 #endif
49 
50 #if HAVE_NICE && HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 
54 /* {{{ php_exec
55  * If type==0, only last line of output is returned (exec)
56  * If type==1, all lines will be printed and last lined returned (system)
57  * If type==2, all lines will be saved to given array (exec with &$array)
58  * If type==3, output will be printed binary, no lines will be saved or returned (passthru)
59  *
60  */
php_exec(int type,char * cmd,zval * array,zval * return_value TSRMLS_DC)61 PHPAPI int php_exec(int type, char *cmd, zval *array, zval *return_value TSRMLS_DC)
62 {
63 	FILE *fp;
64 	char *buf, *tmp=NULL;
65 	int l = 0, pclose_return;
66 	char *cmd_p, *b, *c, *d=NULL;
67 	php_stream *stream;
68 	size_t buflen, bufl = 0;
69 #if PHP_SIGCHILD
70 	void (*sig_handler)() = NULL;
71 #endif
72 
73 	if (PG(safe_mode)) {
74 		if ((c = strchr(cmd, ' '))) {
75 			*c = '\0';
76 			c++;
77 		}
78 		if (strstr(cmd, "..")) {
79 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");
80 			goto err;
81 		}
82 
83 		b = strrchr(cmd, PHP_DIR_SEPARATOR);
84 
85 #ifdef PHP_WIN32
86 		if (b && *b == '\\' && b == cmd) {
87 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid absolute path.");
88 			goto err;
89 		}
90 #endif
91 
92 		spprintf(&d, 0, "%s%s%s%s%s", PG(safe_mode_exec_dir), (b ? "" : "/"), (b ? b : cmd), (c ? " " : ""), (c ? c : ""));
93 		if (c) {
94 			*(c - 1) = ' ';
95 		}
96 		cmd_p = php_escape_shell_cmd(d);
97 		efree(d);
98 		d = cmd_p;
99 	} else {
100 		cmd_p = cmd;
101 	}
102 
103 #if PHP_SIGCHILD
104 	sig_handler = signal (SIGCHLD, SIG_DFL);
105 #endif
106 
107 #ifdef PHP_WIN32
108 	fp = VCWD_POPEN(cmd_p, "rb");
109 #else
110 	fp = VCWD_POPEN(cmd_p, "r");
111 #endif
112 	if (!fp) {
113 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
114 		goto err;
115 	}
116 
117 	stream = php_stream_fopen_from_pipe(fp, "rb");
118 
119 	buf = (char *) emalloc(EXEC_INPUT_BUF);
120 	buflen = EXEC_INPUT_BUF;
121 
122 	if (type != 3) {
123 		b = buf;
124 
125 		while (php_stream_get_line(stream, b, EXEC_INPUT_BUF, &bufl)) {
126 			/* no new line found, let's read some more */
127 			if (b[bufl - 1] != '\n' && !php_stream_eof(stream)) {
128 				if (buflen < (bufl + (b - buf) + EXEC_INPUT_BUF)) {
129 					bufl += b - buf;
130 					buflen = bufl + EXEC_INPUT_BUF;
131 					buf = erealloc(buf, buflen);
132 					b = buf + bufl;
133 				} else {
134 					b += bufl;
135 				}
136 				continue;
137 			} else if (b != buf) {
138 				bufl += b - buf;
139 			}
140 
141 			if (type == 1) {
142 				PHPWRITE(buf, bufl);
143 				if (OG(ob_nesting_level) < 1) {
144 					sapi_flush(TSRMLS_C);
145 				}
146 			} else if (type == 2) {
147 				/* strip trailing whitespaces */
148 				l = bufl;
149 				while (l-- && isspace(((unsigned char *)buf)[l]));
150 				if (l != (int)(bufl - 1)) {
151 					bufl = l + 1;
152 					buf[bufl] = '\0';
153 				}
154 				add_next_index_stringl(array, buf, bufl, 1);
155 			}
156 			b = buf;
157 		}
158 		if (bufl) {
159 			/* strip trailing whitespaces if we have not done so already */
160 			if ((type == 2 && buf != b) || type != 2) {
161 				l = bufl;
162 				while (l-- && isspace(((unsigned char *)buf)[l]));
163 				if (l != (int)(bufl - 1)) {
164 					bufl = l + 1;
165 					buf[bufl] = '\0';
166 				}
167 				if (type == 2) {
168 					add_next_index_stringl(array, buf, bufl, 1);
169 				}
170 			}
171 
172 			/* Return last line from the shell command */
173 			if (PG(magic_quotes_runtime)) {
174 				int len;
175 
176 				tmp = php_addslashes(buf, bufl, &len, 0 TSRMLS_CC);
177 				RETVAL_STRINGL(tmp, len, 0);
178 			} else {
179 				RETVAL_STRINGL(buf, bufl, 1);
180 			}
181 		} else { /* should return NULL, but for BC we return "" */
182 			RETVAL_EMPTY_STRING();
183 		}
184 	} else {
185 		while((bufl = php_stream_read(stream, buf, EXEC_INPUT_BUF)) > 0) {
186 			PHPWRITE(buf, bufl);
187 		}
188 	}
189 
190 	pclose_return = php_stream_close(stream);
191 	efree(buf);
192 
193 done:
194 #if PHP_SIGCHILD
195 	if (sig_handler) {
196 		signal(SIGCHLD, sig_handler);
197 	}
198 #endif
199 	if (d) {
200 		efree(d);
201 	}
202 	return pclose_return;
203 err:
204 	pclose_return = -1;
205 	goto done;
206 }
207 /* }}} */
208 
php_exec_ex(INTERNAL_FUNCTION_PARAMETERS,int mode)209 static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
210 {
211 	char *cmd;
212 	int cmd_len;
213 	zval *ret_code=NULL, *ret_array=NULL;
214 	int ret;
215 
216 	if (mode) {
217 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/", &cmd, &cmd_len, &ret_code) == FAILURE) {
218 			RETURN_FALSE;
219 		}
220 	} else {
221 		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/z/", &cmd, &cmd_len, &ret_array, &ret_code) == FAILURE) {
222 			RETURN_FALSE;
223 		}
224 	}
225 	if (!cmd_len) {
226 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute a blank command");
227 		RETURN_FALSE;
228 	}
229 
230 	if (!ret_array) {
231 		ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC);
232 	} else {
233 		if (Z_TYPE_P(ret_array) != IS_ARRAY) {
234 			zval_dtor(ret_array);
235 			array_init(ret_array);
236 		}
237 		ret = php_exec(2, cmd, ret_array, return_value TSRMLS_CC);
238 	}
239 	if (ret_code) {
240 		zval_dtor(ret_code);
241 		ZVAL_LONG(ret_code, ret);
242 	}
243 }
244 /* }}} */
245 
246 /* {{{ proto string exec(string command [, array &output [, int &return_value]])
247    Execute an external program */
PHP_FUNCTION(exec)248 PHP_FUNCTION(exec)
249 {
250 	php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
251 }
252 /* }}} */
253 
254 /* {{{ proto int system(string command [, int &return_value])
255    Execute an external program and display output */
PHP_FUNCTION(system)256 PHP_FUNCTION(system)
257 {
258 	php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
259 }
260 /* }}} */
261 
262 /* {{{ proto void passthru(string command [, int &return_value])
263    Execute an external program and display raw output */
PHP_FUNCTION(passthru)264 PHP_FUNCTION(passthru)
265 {
266 	php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
267 }
268 /* }}} */
269 
270 /* {{{ php_escape_shell_cmd
271    Escape all chars that could possibly be used to
272    break out of a shell command
273 
274    This function emalloc's a string and returns the pointer.
275    Remember to efree it when done with it.
276 
277    *NOT* safe for binary strings
278 */
php_escape_shell_cmd(char * str)279 PHPAPI char *php_escape_shell_cmd(char *str)
280 {
281 	register int x, y, l = strlen(str);
282 	char *cmd;
283 	char *p = NULL;
284 	size_t estimate = (2 * l) + 1;
285 
286 	TSRMLS_FETCH();
287 
288 	cmd = safe_emalloc(2, l, 1);
289 
290 	for (x = 0, y = 0; x < l; x++) {
291 		int mb_len = php_mblen(str + x, (l - x));
292 
293 		/* skip non-valid multibyte characters */
294 		if (mb_len < 0) {
295 			continue;
296 		} else if (mb_len > 1) {
297 			memcpy(cmd + y, str + x, mb_len);
298 			y += mb_len;
299 			x += mb_len - 1;
300 			continue;
301 		}
302 
303 		switch (str[x]) {
304 #ifndef PHP_WIN32
305 			case '"':
306 			case '\'':
307 				if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {
308 					/* noop */
309 				} else if (p && *p == str[x]) {
310 					p = NULL;
311 				} else {
312 					cmd[y++] = '\\';
313 				}
314 				cmd[y++] = str[x];
315 				break;
316 #else
317 			/* % is Windows specific for enviromental variables, ^%PATH% will
318 				output PATH whil ^%PATH^% not. escapeshellcmd will escape all %.
319 			*/
320 			case '%':
321 			case '"':
322 			case '\'':
323 #endif
324 			case '#': /* This is character-set independent */
325 			case '&':
326 			case ';':
327 			case '`':
328 			case '|':
329 			case '*':
330 			case '?':
331 			case '~':
332 			case '<':
333 			case '>':
334 			case '^':
335 			case '(':
336 			case ')':
337 			case '[':
338 			case ']':
339 			case '{':
340 			case '}':
341 			case '$':
342 			case '\\':
343 			case '\x0A': /* excluding these two */
344 			case '\xFF':
345 #ifdef PHP_WIN32
346 				cmd[y++] = '^';
347 #else
348 				cmd[y++] = '\\';
349 #endif
350 				/* fall-through */
351 			default:
352 				cmd[y++] = str[x];
353 
354 		}
355 	}
356 	cmd[y] = '\0';
357 
358 	if ((estimate - y) > 4096) {
359 		/* realloc if the estimate was way overill
360 		 * Arbitrary cutoff point of 4096 */
361 		cmd = erealloc(cmd, y + 1);
362 	}
363 
364 	return cmd;
365 }
366 /* }}} */
367 
368 /* {{{ php_escape_shell_arg
369  */
php_escape_shell_arg(char * str)370 PHPAPI char *php_escape_shell_arg(char *str)
371 {
372 	int x, y = 0, l = strlen(str);
373 	char *cmd;
374 	size_t estimate = (4 * l) + 3;
375 
376 	TSRMLS_FETCH();
377 
378 	cmd = safe_emalloc(4, l, 3); /* worst case */
379 
380 #ifdef PHP_WIN32
381 	cmd[y++] = '"';
382 #else
383 	cmd[y++] = '\'';
384 #endif
385 
386 	for (x = 0; x < l; x++) {
387 		int mb_len = php_mblen(str + x, (l - x));
388 
389 		/* skip non-valid multibyte characters */
390 		if (mb_len < 0) {
391 			continue;
392 		} else if (mb_len > 1) {
393 			memcpy(cmd + y, str + x, mb_len);
394 			y += mb_len;
395 			x += mb_len - 1;
396 			continue;
397 		}
398 
399 		switch (str[x]) {
400 #ifdef PHP_WIN32
401 		case '"':
402 		case '%':
403 			cmd[y++] = ' ';
404 			break;
405 #else
406 		case '\'':
407 			cmd[y++] = '\'';
408 			cmd[y++] = '\\';
409 			cmd[y++] = '\'';
410 #endif
411 			/* fall-through */
412 		default:
413 			cmd[y++] = str[x];
414 		}
415 	}
416 #ifdef PHP_WIN32
417 	cmd[y++] = '"';
418 #else
419 	cmd[y++] = '\'';
420 #endif
421 	cmd[y] = '\0';
422 
423 	if ((estimate - y) > 4096) {
424 		/* realloc if the estimate was way overill
425 		 * Arbitrary cutoff point of 4096 */
426 		cmd = erealloc(cmd, y + 1);
427 	}
428 	return cmd;
429 }
430 /* }}} */
431 
432 /* {{{ proto string escapeshellcmd(string command)
433    Escape shell metacharacters */
PHP_FUNCTION(escapeshellcmd)434 PHP_FUNCTION(escapeshellcmd)
435 {
436 	char *command;
437 	int command_len;
438 	char *cmd = NULL;
439 
440 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
441 		return;
442 	}
443 
444 	if (command_len) {
445 		cmd = php_escape_shell_cmd(command);
446 		RETVAL_STRING(cmd, 0);
447 	} else {
448 		RETVAL_EMPTY_STRING();
449 	}
450 }
451 /* }}} */
452 
453 /* {{{ proto string escapeshellarg(string arg)
454    Quote and escape an argument for use in a shell command */
PHP_FUNCTION(escapeshellarg)455 PHP_FUNCTION(escapeshellarg)
456 {
457 	char *argument;
458 	int argument_len;
459 	char *cmd = NULL;
460 
461 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &argument, &argument_len) == FAILURE) {
462 		return;
463 	}
464 
465 	if (argument) {
466 		cmd = php_escape_shell_arg(argument);
467 		RETVAL_STRING(cmd, 0);
468 	}
469 }
470 /* }}} */
471 
472 /* {{{ proto string shell_exec(string cmd)
473    Execute command via shell and return complete output as string */
PHP_FUNCTION(shell_exec)474 PHP_FUNCTION(shell_exec)
475 {
476 	FILE *in;
477 	size_t total_readbytes;
478 	char *command;
479 	int command_len;
480 	char *ret;
481 	php_stream *stream;
482 
483 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
484 		return;
485 	}
486 
487 	if (PG(safe_mode)) {
488 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot execute using backquotes in Safe Mode");
489 		RETURN_FALSE;
490 	}
491 
492 #ifdef PHP_WIN32
493 	if ((in=VCWD_POPEN(command, "rt"))==NULL) {
494 #else
495 	if ((in=VCWD_POPEN(command, "r"))==NULL) {
496 #endif
497 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to execute '%s'", command);
498 		RETURN_FALSE;
499 	}
500 
501 	stream = php_stream_fopen_from_pipe(in, "rb");
502 	total_readbytes = php_stream_copy_to_mem(stream, &ret, PHP_STREAM_COPY_ALL, 0);
503 	php_stream_close(stream);
504 
505 	if (total_readbytes > 0) {
506 		RETVAL_STRINGL(ret, total_readbytes, 0);
507 	}
508 }
509 /* }}} */
510 
511 #ifdef HAVE_NICE
512 /* {{{ proto bool proc_nice(int priority)
513    Change the priority of the current process */
514 PHP_FUNCTION(proc_nice)
515 {
516 	long pri;
517 
518 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &pri) == FAILURE) {
519 		RETURN_FALSE;
520 	}
521 
522 	errno = 0;
523 	nice(pri);
524 	if (errno) {
525 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Only a super user may attempt to increase the priority of a process");
526 		RETURN_FALSE;
527 	}
528 
529 	RETURN_TRUE;
530 }
531 /* }}} */
532 #endif
533 
534 /*
535  * Local variables:
536  * tab-width: 4
537  * c-basic-offset: 4
538  * End:
539  * vim600: sw=4 ts=4 fdm=marker
540  * vim<600: sw=4 ts=4
541  */
542