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