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

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