root/ext/standard/exec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PHP_MINIT_FUNCTION
  2. php_exec
  3. php_exec_ex
  4. PHP_FUNCTION
  5. PHP_FUNCTION
  6. PHP_FUNCTION
  7. php_escape_shell_cmd
  8. php_escape_shell_arg
  9. PHP_FUNCTION
  10. PHP_FUNCTION
  11. PHP_FUNCTION
  12. PHP_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */