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: 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_UNISTD_H
50 #include <unistd.h>
51 #endif
52
53 #if HAVE_LIMITS_H
54 #include <limits.h>
55 #endif
56
57 #ifdef PHP_WIN32
58 # include "win32/nice.h"
59 #endif
60
61 static size_t cmd_max_len;
62
63 /* {{{ PHP_MINIT_FUNCTION(exec) */
PHP_MINIT_FUNCTION(exec)64 PHP_MINIT_FUNCTION(exec)
65 {
66 #ifdef _SC_ARG_MAX
67 cmd_max_len = sysconf(_SC_ARG_MAX);
68 if ((size_t)-1 == cmd_max_len) {
69 #ifdef _POSIX_ARG_MAX
70 cmd_max_len = _POSIX_ARG_MAX;
71 #else
72 cmd_max_len = 4096;
73 #endif
74 }
75 #elif defined(ARG_MAX)
76 cmd_max_len = ARG_MAX;
77 #elif defined(PHP_WIN32)
78 /* Executed commands will run through cmd.exe. As long as it's the case,
79 it's just the constant limit.*/
80 cmd_max_len = 8192;
81 #else
82 /* This is just an arbitrary value for the fallback case. */
83 cmd_max_len = 4096;
84 #endif
85
86 return SUCCESS;
87 }
88 /* }}} */
89
90 /* {{{ php_exec
91 * If type==0, only last line of output is returned (exec)
92 * If type==1, all lines will be printed and last lined returned (system)
93 * If type==2, all lines will be saved to given array (exec with &$array)
94 * If type==3, output will be printed binary, no lines will be saved or returned (passthru)
95 *
96 */
php_exec(int type,char * cmd,zval * array,zval * return_value)97 PHPAPI int php_exec(int type, char *cmd, zval *array, zval *return_value)
98 {
99 FILE *fp;
100 char *buf;
101 size_t l = 0;
102 int pclose_return;
103 char *b, *d=NULL;
104 php_stream *stream;
105 size_t buflen, bufl = 0;
106 #if PHP_SIGCHILD
107 void (*sig_handler)() = NULL;
108 #endif
109
110 #if PHP_SIGCHILD
111 sig_handler = signal (SIGCHLD, SIG_DFL);
112 #endif
113
114 #ifdef PHP_WIN32
115 fp = VCWD_POPEN(cmd, "rb");
116 #else
117 fp = VCWD_POPEN(cmd, "r");
118 #endif
119 if (!fp) {
120 php_error_docref(NULL, E_WARNING, "Unable to fork [%s]", cmd);
121 goto err;
122 }
123
124 stream = php_stream_fopen_from_pipe(fp, "rb");
125
126 buf = (char *) emalloc(EXEC_INPUT_BUF);
127 buflen = EXEC_INPUT_BUF;
128
129 if (type != 3) {
130 b = buf;
131
132 while (php_stream_get_line(stream, b, EXEC_INPUT_BUF, &bufl)) {
133 /* no new line found, let's read some more */
134 if (b[bufl - 1] != '\n' && !php_stream_eof(stream)) {
135 if (buflen < (bufl + (b - buf) + EXEC_INPUT_BUF)) {
136 bufl += b - buf;
137 buflen = bufl + EXEC_INPUT_BUF;
138 buf = erealloc(buf, buflen);
139 b = buf + bufl;
140 } else {
141 b += bufl;
142 }
143 continue;
144 } else if (b != buf) {
145 bufl += b - buf;
146 }
147
148 if (type == 1) {
149 PHPWRITE(buf, bufl);
150 if (php_output_get_level() < 1) {
151 sapi_flush();
152 }
153 } else if (type == 2) {
154 /* strip trailing whitespaces */
155 l = bufl;
156 while (l-- > 0 && isspace(((unsigned char *)buf)[l]));
157 if (l != (bufl - 1)) {
158 bufl = l + 1;
159 buf[bufl] = '\0';
160 }
161 add_next_index_stringl(array, buf, bufl);
162 }
163 b = buf;
164 }
165 if (bufl) {
166 /* strip trailing whitespaces if we have not done so already */
167 if ((type == 2 && buf != b) || type != 2) {
168 l = bufl;
169 while (l-- > 0 && isspace(((unsigned char *)buf)[l]));
170 if (l != (bufl - 1)) {
171 bufl = l + 1;
172 buf[bufl] = '\0';
173 }
174 if (type == 2) {
175 add_next_index_stringl(array, buf, bufl);
176 }
177 }
178
179 /* Return last line from the shell command */
180 RETVAL_STRINGL(buf, bufl);
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 size_t cmd_len;
213 zval *ret_code=NULL, *ret_array=NULL;
214 int ret;
215
216 ZEND_PARSE_PARAMETERS_START(1, (mode ? 2 : 3))
217 Z_PARAM_STRING(cmd, cmd_len)
218 Z_PARAM_OPTIONAL
219 if (!mode) {
220 Z_PARAM_ZVAL_DEREF(ret_array)
221 }
222 Z_PARAM_ZVAL_DEREF(ret_code)
223 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
224
225 if (!cmd_len) {
226 php_error_docref(NULL, E_WARNING, "Cannot execute a blank command");
227 RETURN_FALSE;
228 }
229 if (strlen(cmd) != cmd_len) {
230 php_error_docref(NULL, E_WARNING, "NULL byte detected. Possible attack");
231 RETURN_FALSE;
232 }
233
234 if (!ret_array) {
235 ret = php_exec(mode, cmd, NULL, return_value);
236 } else {
237 if (Z_TYPE_P(ret_array) != IS_ARRAY) {
238 zval_ptr_dtor(ret_array);
239 array_init(ret_array);
240 } else if (Z_REFCOUNT_P(ret_array) > 1) {
241 zval_ptr_dtor(ret_array);
242 ZVAL_ARR(ret_array, zend_array_dup(Z_ARR_P(ret_array)));
243 }
244 ret = php_exec(2, cmd, ret_array, return_value);
245 }
246 if (ret_code) {
247 zval_ptr_dtor(ret_code);
248 ZVAL_LONG(ret_code, ret);
249 }
250 }
251 /* }}} */
252
253 /* {{{ proto string exec(string command [, array &output [, int &return_value]])
254 Execute an external program */
PHP_FUNCTION(exec)255 PHP_FUNCTION(exec)
256 {
257 php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
258 }
259 /* }}} */
260
261 /* {{{ proto int system(string command [, int &return_value])
262 Execute an external program and display output */
PHP_FUNCTION(system)263 PHP_FUNCTION(system)
264 {
265 php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
266 }
267 /* }}} */
268
269 /* {{{ proto void passthru(string command [, int &return_value])
270 Execute an external program and display raw output */
PHP_FUNCTION(passthru)271 PHP_FUNCTION(passthru)
272 {
273 php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
274 }
275 /* }}} */
276
277 /* {{{ php_escape_shell_cmd
278 Escape all chars that could possibly be used to
279 break out of a shell command
280
281 This function emalloc's a string and returns the pointer.
282 Remember to efree it when done with it.
283
284 *NOT* safe for binary strings
285 */
php_escape_shell_cmd(char * str)286 PHPAPI zend_string *php_escape_shell_cmd(char *str)
287 {
288 register size_t x, y;
289 size_t l = strlen(str);
290 uint64_t estimate = (2 * (uint64_t)l) + 1;
291 zend_string *cmd;
292 #ifndef PHP_WIN32
293 char *p = NULL;
294 #endif
295
296 /* max command line length - two single quotes - \0 byte length */
297 if (l > cmd_max_len - 2 - 1) {
298 php_error_docref(NULL, E_ERROR, "Command exceeds the allowed length of %zu bytes", cmd_max_len);
299 return ZSTR_EMPTY_ALLOC();
300 }
301
302 cmd = zend_string_safe_alloc(2, l, 0, 0);
303
304 for (x = 0, y = 0; x < l; x++) {
305 int mb_len = php_mblen(str + x, (l - x));
306
307 /* skip non-valid multibyte characters */
308 if (mb_len < 0) {
309 continue;
310 } else if (mb_len > 1) {
311 memcpy(ZSTR_VAL(cmd) + y, str + x, mb_len);
312 y += mb_len;
313 x += mb_len - 1;
314 continue;
315 }
316
317 switch (str[x]) {
318 #ifndef PHP_WIN32
319 case '"':
320 case '\'':
321 if (!p && (p = memchr(str + x + 1, str[x], l - x - 1))) {
322 /* noop */
323 } else if (p && *p == str[x]) {
324 p = NULL;
325 } else {
326 ZSTR_VAL(cmd)[y++] = '\\';
327 }
328 ZSTR_VAL(cmd)[y++] = str[x];
329 break;
330 #else
331 /* % is Windows specific for environmental variables, ^%PATH% will
332 output PATH while ^%PATH^% will not. escapeshellcmd->val will escape all % and !.
333 */
334 case '%':
335 case '!':
336 case '"':
337 case '\'':
338 #endif
339 case '#': /* This is character-set independent */
340 case '&':
341 case ';':
342 case '`':
343 case '|':
344 case '*':
345 case '?':
346 case '~':
347 case '<':
348 case '>':
349 case '^':
350 case '(':
351 case ')':
352 case '[':
353 case ']':
354 case '{':
355 case '}':
356 case '$':
357 case '\\':
358 case '\x0A': /* excluding these two */
359 case '\xFF':
360 #ifdef PHP_WIN32
361 ZSTR_VAL(cmd)[y++] = '^';
362 #else
363 ZSTR_VAL(cmd)[y++] = '\\';
364 #endif
365 /* fall-through */
366 default:
367 ZSTR_VAL(cmd)[y++] = str[x];
368
369 }
370 }
371 ZSTR_VAL(cmd)[y] = '\0';
372
373 if (y > cmd_max_len + 1) {
374 php_error_docref(NULL, E_ERROR, "Escaped command exceeds the allowed length of %zu bytes", cmd_max_len);
375 zend_string_release(cmd);
376 return ZSTR_EMPTY_ALLOC();
377 }
378
379 if ((estimate - y) > 4096) {
380 /* realloc if the estimate was way overill
381 * Arbitrary cutoff point of 4096 */
382 cmd = zend_string_truncate(cmd, y, 0);
383 }
384
385 ZSTR_LEN(cmd) = y;
386
387 return cmd;
388 }
389 /* }}} */
390
391 /* {{{ php_escape_shell_arg
392 */
php_escape_shell_arg(char * str)393 PHPAPI zend_string *php_escape_shell_arg(char *str)
394 {
395 size_t x, y = 0;
396 size_t l = strlen(str);
397 zend_string *cmd;
398 uint64_t estimate = (4 * (uint64_t)l) + 3;
399
400 /* max command line length - two single quotes - \0 byte length */
401 if (l > cmd_max_len - 2 - 1) {
402 php_error_docref(NULL, E_ERROR, "Argument exceeds the allowed length of %zu bytes", cmd_max_len);
403 return ZSTR_EMPTY_ALLOC();
404 }
405
406 cmd = zend_string_safe_alloc(4, l, 2, 0); /* worst case */
407
408 #ifdef PHP_WIN32
409 ZSTR_VAL(cmd)[y++] = '"';
410 #else
411 ZSTR_VAL(cmd)[y++] = '\'';
412 #endif
413
414 for (x = 0; x < l; x++) {
415 int mb_len = php_mblen(str + x, (l - x));
416
417 /* skip non-valid multibyte characters */
418 if (mb_len < 0) {
419 continue;
420 } else if (mb_len > 1) {
421 memcpy(ZSTR_VAL(cmd) + y, str + x, mb_len);
422 y += mb_len;
423 x += mb_len - 1;
424 continue;
425 }
426
427 switch (str[x]) {
428 #ifdef PHP_WIN32
429 case '"':
430 case '%':
431 case '!':
432 ZSTR_VAL(cmd)[y++] = ' ';
433 break;
434 #else
435 case '\'':
436 ZSTR_VAL(cmd)[y++] = '\'';
437 ZSTR_VAL(cmd)[y++] = '\\';
438 ZSTR_VAL(cmd)[y++] = '\'';
439 #endif
440 /* fall-through */
441 default:
442 ZSTR_VAL(cmd)[y++] = str[x];
443 }
444 }
445 #ifdef PHP_WIN32
446 if (y > 0 && '\\' == ZSTR_VAL(cmd)[y - 1]) {
447 int k = 0, n = y - 1;
448 for (; n >= 0 && '\\' == ZSTR_VAL(cmd)[n]; n--, k++);
449 if (k % 2) {
450 ZSTR_VAL(cmd)[y++] = '\\';
451 }
452 }
453
454 ZSTR_VAL(cmd)[y++] = '"';
455 #else
456 ZSTR_VAL(cmd)[y++] = '\'';
457 #endif
458 ZSTR_VAL(cmd)[y] = '\0';
459
460 if (y > cmd_max_len + 1) {
461 php_error_docref(NULL, E_ERROR, "Escaped argument exceeds the allowed length of %zu bytes", cmd_max_len);
462 zend_string_release(cmd);
463 return ZSTR_EMPTY_ALLOC();
464 }
465
466 if ((estimate - y) > 4096) {
467 /* realloc if the estimate was way overill
468 * Arbitrary cutoff point of 4096 */
469 cmd = zend_string_truncate(cmd, y, 0);
470 }
471 ZSTR_LEN(cmd) = y;
472 return cmd;
473 }
474 /* }}} */
475
476 /* {{{ proto string escapeshellcmd(string command)
477 Escape shell metacharacters */
PHP_FUNCTION(escapeshellcmd)478 PHP_FUNCTION(escapeshellcmd)
479 {
480 char *command;
481 size_t command_len;
482
483 ZEND_PARSE_PARAMETERS_START(1, 1)
484 Z_PARAM_STRING(command, command_len)
485 ZEND_PARSE_PARAMETERS_END();
486
487 if (command_len) {
488 if (command_len != strlen(command)) {
489 php_error_docref(NULL, E_ERROR, "Input string contains NULL bytes");
490 return;
491 }
492 RETVAL_STR(php_escape_shell_cmd(command));
493 } else {
494 RETVAL_EMPTY_STRING();
495 }
496 }
497 /* }}} */
498
499 /* {{{ proto string escapeshellarg(string arg)
500 Quote and escape an argument for use in a shell command */
PHP_FUNCTION(escapeshellarg)501 PHP_FUNCTION(escapeshellarg)
502 {
503 char *argument;
504 size_t argument_len;
505
506 ZEND_PARSE_PARAMETERS_START(1, 1)
507 Z_PARAM_STRING(argument, argument_len)
508 ZEND_PARSE_PARAMETERS_END();
509
510 if (argument) {
511 if (argument_len != strlen(argument)) {
512 php_error_docref(NULL, E_ERROR, "Input string contains NULL bytes");
513 return;
514 }
515 RETVAL_STR(php_escape_shell_arg(argument));
516 }
517 }
518 /* }}} */
519
520 /* {{{ proto string shell_exec(string cmd)
521 Execute command via shell and return complete output as string */
PHP_FUNCTION(shell_exec)522 PHP_FUNCTION(shell_exec)
523 {
524 FILE *in;
525 char *command;
526 size_t command_len;
527 zend_string *ret;
528 php_stream *stream;
529
530 ZEND_PARSE_PARAMETERS_START(1, 1)
531 Z_PARAM_STRING(command, command_len)
532 ZEND_PARSE_PARAMETERS_END();
533
534 if (!command_len) {
535 php_error_docref(NULL, E_WARNING, "Cannot execute a blank command");
536 RETURN_FALSE;
537 }
538 if (strlen(command) != command_len) {
539 php_error_docref(NULL, E_WARNING, "NULL byte detected. Possible attack");
540 RETURN_FALSE;
541 }
542
543 #ifdef PHP_WIN32
544 if ((in=VCWD_POPEN(command, "rt"))==NULL) {
545 #else
546 if ((in=VCWD_POPEN(command, "r"))==NULL) {
547 #endif
548 php_error_docref(NULL, E_WARNING, "Unable to execute '%s'", command);
549 RETURN_FALSE;
550 }
551
552 stream = php_stream_fopen_from_pipe(in, "rb");
553 ret = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
554 php_stream_close(stream);
555
556 if (ret && ZSTR_LEN(ret) > 0) {
557 RETVAL_STR(ret);
558 }
559 }
560 /* }}} */
561
562 #ifdef HAVE_NICE
563 /* {{{ proto bool proc_nice(int priority)
564 Change the priority of the current process */
565 PHP_FUNCTION(proc_nice)
566 {
567 zend_long pri;
568
569 ZEND_PARSE_PARAMETERS_START(1, 1)
570 Z_PARAM_LONG(pri)
571 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
572
573 errno = 0;
574 php_ignore_value(nice(pri));
575 if (errno) {
576 #ifdef PHP_WIN32
577 php_error_docref(NULL, E_WARNING, "%s", php_win_err());
578 #else
579 php_error_docref(NULL, E_WARNING, "Only a super user may attempt to increase the priority of a process");
580 #endif
581 RETURN_FALSE;
582 }
583
584 RETURN_TRUE;
585 }
586 /* }}} */
587 #endif
588
589 /*
590 * Local variables:
591 * tab-width: 4
592 * c-basic-offset: 4
593 * End:
594 * vim600: sw=4 ts=4 fdm=marker
595 * vim<600: sw=4 ts=4
596 */
597