ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SSH2.php
Go to the documentation of this file.
1 <?php
2 
50 namespace phpseclib\Net;
51 
61 use phpseclib\Math\BigInteger; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification.
63 
71 class SSH2
72 {
79  const MASK_CONSTRUCTOR = 0x00000001;
80  const MASK_CONNECTED = 0x00000002;
81  const MASK_LOGIN_REQ = 0x00000004;
82  const MASK_LOGIN = 0x00000008;
83  const MASK_SHELL = 0x00000010;
84  const MASK_WINDOW_ADJUST = 0x00000020;
103  const CHANNEL_EXEC = 0; // PuTTy uses 0x100
104  const CHANNEL_SHELL = 1;
105  const CHANNEL_SUBSYSTEM = 2;
116  const LOG_SIMPLE = 1;
120  const LOG_COMPLEX = 2;
124  const LOG_REALTIME = 3;
128  const LOG_REALTIME_FILE = 4;
138  const READ_SIMPLE = 1;
142  const READ_REGEX = 2;
146  const LOG_MAX_SIZE = 1048576; // 1024 * 1024
156 
163  var $fsock;
164 
174  var $bitmap = 0;
175 
184  var $errors = array();
185 
193  var $server_identifier = false;
194 
202  var $kex_algorithms = false;
203 
212 
221 
230 
239 
248 
257 
266 
275 
284 
293 
302 
311 
328 
338 
346  var $decrypt = false;
347 
355  var $encrypt = false;
356 
364  var $hmac_create = false;
365 
373  var $hmac_check = false;
374 
386  var $hmac_size = false;
387 
396 
410  var $session_id = false;
411 
421  var $exchange_hash = false;
422 
430  var $message_numbers = array();
431 
439  var $disconnect_reasons = array();
440 
449 
458  var $terminal_modes = array();
459 
469 
479  var $send_seq_no = 0;
480 
490  var $get_seq_no = 0;
491 
502  var $server_channels = array();
503 
515  var $channel_buffers = array();
516 
526  var $channel_status = array();
527 
538 
546  var $message_number_log = array();
547 
555  var $message_log = array();
556 
567  var $window_size = 0x7FFFFFFF;
568 
579 
590 
600  var $signature = '';
601 
612 
621 
633 
640  var $timeout;
641 
649 
658 
667 
675  var $signature_validated = false;
676 
684 
691  var $quiet_mode = false;
692 
700 
708 
716  var $request_pty = false;
717 
724  var $in_request_pty_exec = false;
725 
733 
741 
750 
759 
771  var $banner_message = '';
772 
780  var $is_timeout = false;
781 
789  var $log_boundary = ':';
790 
798  var $log_long_width = 65;
799 
808 
817  var $host;
818 
827  var $port;
828 
838  var $windowColumns = 80;
839 
849  var $windowRows = 24;
850 
859  var $crypto_engine = false;
860 
867  var $agent;
868 
881  function __construct($host, $port = 22, $timeout = 10)
882  {
883  $this->message_numbers = array(
884  1 => 'NET_SSH2_MSG_DISCONNECT',
885  2 => 'NET_SSH2_MSG_IGNORE',
886  3 => 'NET_SSH2_MSG_UNIMPLEMENTED',
887  4 => 'NET_SSH2_MSG_DEBUG',
888  5 => 'NET_SSH2_MSG_SERVICE_REQUEST',
889  6 => 'NET_SSH2_MSG_SERVICE_ACCEPT',
890  20 => 'NET_SSH2_MSG_KEXINIT',
891  21 => 'NET_SSH2_MSG_NEWKEYS',
892  30 => 'NET_SSH2_MSG_KEXDH_INIT',
893  31 => 'NET_SSH2_MSG_KEXDH_REPLY',
894  50 => 'NET_SSH2_MSG_USERAUTH_REQUEST',
895  51 => 'NET_SSH2_MSG_USERAUTH_FAILURE',
896  52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS',
897  53 => 'NET_SSH2_MSG_USERAUTH_BANNER',
898 
899  80 => 'NET_SSH2_MSG_GLOBAL_REQUEST',
900  81 => 'NET_SSH2_MSG_REQUEST_SUCCESS',
901  82 => 'NET_SSH2_MSG_REQUEST_FAILURE',
902  90 => 'NET_SSH2_MSG_CHANNEL_OPEN',
903  91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION',
904  92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE',
905  93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST',
906  94 => 'NET_SSH2_MSG_CHANNEL_DATA',
907  95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA',
908  96 => 'NET_SSH2_MSG_CHANNEL_EOF',
909  97 => 'NET_SSH2_MSG_CHANNEL_CLOSE',
910  98 => 'NET_SSH2_MSG_CHANNEL_REQUEST',
911  99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS',
912  100 => 'NET_SSH2_MSG_CHANNEL_FAILURE'
913  );
914  $this->disconnect_reasons = array(
915  1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT',
916  2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR',
917  3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED',
918  4 => 'NET_SSH2_DISCONNECT_RESERVED',
919  5 => 'NET_SSH2_DISCONNECT_MAC_ERROR',
920  6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR',
921  7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE',
922  8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED',
923  9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE',
924  10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST',
925  11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION',
926  12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS',
927  13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER',
928  14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE',
929  15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME'
930  );
931  $this->channel_open_failure_reasons = array(
932  1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED'
933  );
934  $this->terminal_modes = array(
935  0 => 'NET_SSH2_TTY_OP_END'
936  );
937  $this->channel_extended_data_type_codes = array(
938  1 => 'NET_SSH2_EXTENDED_DATA_STDERR'
939  );
940 
941  $this->_define_array(
942  $this->message_numbers,
943  $this->disconnect_reasons,
944  $this->channel_open_failure_reasons,
945  $this->terminal_modes,
946  $this->channel_extended_data_type_codes,
947  array(60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'),
948  array(60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'),
949  array(60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
950  61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'),
951  // RFC 4419 - diffie-hellman-group-exchange-sha{1,256}
952  array(30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD',
953  31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP',
954  32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT',
955  33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY',
956  34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'),
957  // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org)
958  array(30 => 'NET_SSH2_MSG_KEX_ECDH_INIT',
959  31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY')
960  );
961 
962  if (is_resource($host)) {
963  $this->fsock = $host;
964  return;
965  }
966 
967  if (is_string($host)) {
968  $this->host = $host;
969  $this->port = $port;
970  $this->timeout = $timeout;
971  }
972  }
973 
984  {
985  $this->crypto_engine = $engine;
986  }
987 
994  function _connect()
995  {
996  if ($this->bitmap & self::MASK_CONSTRUCTOR) {
997  return false;
998  }
999 
1000  $this->bitmap |= self::MASK_CONSTRUCTOR;
1001 
1002  $this->curTimeout = $this->timeout;
1003 
1004  $this->last_packet = microtime(true);
1005 
1006  if (!is_resource($this->fsock)) {
1007  $start = microtime(true);
1008  $this->fsock = @fsockopen($this->host, $this->port, $errno, $errstr, $this->curTimeout);
1009  if (!$this->fsock) {
1010  $host = $this->host . ':' . $this->port;
1011  user_error(rtrim("Cannot connect to $host. Error $errno. $errstr"));
1012  return false;
1013  }
1014  $elapsed = microtime(true) - $start;
1015 
1016  $this->curTimeout-= $elapsed;
1017 
1018  if ($this->curTimeout <= 0) {
1019  $this->is_timeout = true;
1020  return false;
1021  }
1022  }
1023 
1024  /* According to the SSH2 specs,
1025 
1026  "The server MAY send other lines of data before sending the version
1027  string. Each line SHOULD be terminated by a Carriage Return and Line
1028  Feed. Such lines MUST NOT begin with "SSH-", and SHOULD be encoded
1029  in ISO-10646 UTF-8 [RFC3629] (language is not specified). Clients
1030  MUST be able to process such lines." */
1031  $temp = '';
1032  $extra = '';
1033  while (!feof($this->fsock) && !preg_match('#^SSH-(\d\.\d+)#', $temp, $matches)) {
1034  if (substr($temp, -2) == "\r\n") {
1035  $extra.= $temp;
1036  $temp = '';
1037  }
1038 
1039  if ($this->curTimeout) {
1040  if ($this->curTimeout < 0) {
1041  $this->is_timeout = true;
1042  return false;
1043  }
1044  $read = array($this->fsock);
1045  $write = $except = null;
1046  $start = microtime(true);
1047  $sec = floor($this->curTimeout);
1048  $usec = 1000000 * ($this->curTimeout - $sec);
1049  // on windows this returns a "Warning: Invalid CRT parameters detected" error
1050  // the !count() is done as a workaround for <https://bugs.php.net/42682>
1051  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
1052  $this->is_timeout = true;
1053  return false;
1054  }
1055  $elapsed = microtime(true) - $start;
1056  $this->curTimeout-= $elapsed;
1057  }
1058 
1059  $temp.= fgets($this->fsock, 255);
1060  }
1061 
1062  if (feof($this->fsock)) {
1063  user_error('Connection closed by server');
1064  return false;
1065  }
1066 
1067  $this->identifier = $this->_generate_identifier();
1068 
1069  if (defined('NET_SSH2_LOGGING')) {
1070  $this->_append_log('<-', $extra . $temp);
1071  $this->_append_log('->', $this->identifier . "\r\n");
1072  }
1073 
1074  $this->server_identifier = trim($temp, "\r\n");
1075  if (strlen($extra)) {
1076  $this->errors[] = utf8_decode($extra);
1077  }
1078 
1079  if ($matches[1] != '1.99' && $matches[1] != '2.0') {
1080  user_error("Cannot connect to SSH $matches[1] servers");
1081  return false;
1082  }
1083 
1084  fputs($this->fsock, $this->identifier . "\r\n");
1085 
1086  $response = $this->_get_binary_packet();
1087  if ($response === false) {
1088  user_error('Connection closed by server');
1089  return false;
1090  }
1091 
1092  if (ord($response[0]) != NET_SSH2_MSG_KEXINIT) {
1093  user_error('Expected SSH_MSG_KEXINIT');
1094  return false;
1095  }
1096 
1097  if (!$this->_key_exchange($response)) {
1098  return false;
1099  }
1100 
1101  $this->bitmap|= self::MASK_CONNECTED;
1102 
1103  return true;
1104  }
1105 
1115  {
1116  $identifier = 'SSH-2.0-phpseclib_2.0';
1117 
1118  $ext = array();
1119  if (extension_loaded('libsodium')) {
1120  $ext[] = 'libsodium';
1121  }
1122 
1123  if (extension_loaded('openssl')) {
1124  $ext[] = 'openssl';
1125  } elseif (extension_loaded('mcrypt')) {
1126  $ext[] = 'mcrypt';
1127  }
1128 
1129  if (extension_loaded('gmp')) {
1130  $ext[] = 'gmp';
1131  } elseif (extension_loaded('bcmath')) {
1132  $ext[] = 'bcmath';
1133  }
1134 
1135  if (!empty($ext)) {
1136  $identifier .= ' (' . implode(', ', $ext) . ')';
1137  }
1138 
1139  return $identifier;
1140  }
1141 
1148  function _key_exchange($kexinit_payload_server)
1149  {
1150  $kex_algorithms = array(
1151  // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using
1152  // Curve25519. See doc/curve25519-sha256@libssh.org.txt in the
1153  // libssh repository for more information.
1154  'curve25519-sha256@libssh.org',
1155 
1156  // Diffie-Hellman Key Agreement (DH) using integer modulo prime
1157  // groups.
1158  'diffie-hellman-group1-sha1', // REQUIRED
1159  'diffie-hellman-group14-sha1', // REQUIRED
1160  'diffie-hellman-group-exchange-sha1', // RFC 4419
1161  'diffie-hellman-group-exchange-sha256', // RFC 4419
1162  );
1163  if (!class_exists('\Sodium')) {
1164  $kex_algorithms = array_diff(
1165  $kex_algorithms,
1166  array('curve25519-sha256@libssh.org')
1167  );
1168  }
1169 
1170  $server_host_key_algorithms = array(
1171  'ssh-rsa', // RECOMMENDED sign Raw RSA Key
1172  'ssh-dss' // REQUIRED sign Raw DSS Key
1173  );
1174 
1175  $encryption_algorithms = array(
1176  // from <http://tools.ietf.org/html/rfc4345#section-4>:
1177  'arcfour256',
1178  'arcfour128',
1179 
1180  //'arcfour', // OPTIONAL the ARCFOUR stream cipher with a 128-bit key
1181 
1182  // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>:
1183  'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key
1184  'aes192-ctr', // RECOMMENDED AES with 192-bit key
1185  'aes256-ctr', // RECOMMENDED AES with 256-bit key
1186 
1187  'twofish128-ctr', // OPTIONAL Twofish in SDCTR mode, with 128-bit key
1188  'twofish192-ctr', // OPTIONAL Twofish with 192-bit key
1189  'twofish256-ctr', // OPTIONAL Twofish with 256-bit key
1190 
1191  'aes128-cbc', // RECOMMENDED AES with a 128-bit key
1192  'aes192-cbc', // OPTIONAL AES with a 192-bit key
1193  'aes256-cbc', // OPTIONAL AES in CBC mode, with a 256-bit key
1194 
1195  'twofish128-cbc', // OPTIONAL Twofish with a 128-bit key
1196  'twofish192-cbc', // OPTIONAL Twofish with a 192-bit key
1197  'twofish256-cbc',
1198  'twofish-cbc', // OPTIONAL alias for "twofish256-cbc"
1199  // (this is being retained for historical reasons)
1200 
1201  'blowfish-ctr', // OPTIONAL Blowfish in SDCTR mode
1202 
1203  'blowfish-cbc', // OPTIONAL Blowfish in CBC mode
1204 
1205  '3des-ctr', // RECOMMENDED Three-key 3DES in SDCTR mode
1206 
1207  '3des-cbc', // REQUIRED three-key 3DES in CBC mode
1208  //'none' // OPTIONAL no encryption; NOT RECOMMENDED
1209  );
1210 
1211  if (extension_loaded('openssl') && !extension_loaded('mcrypt')) {
1212  // OpenSSL does not support arcfour256 in any capacity and arcfour128 / arcfour support is limited to
1213  // instances that do not use continuous buffers
1214  $encryption_algorithms = array_diff(
1215  $encryption_algorithms,
1216  array('arcfour256', 'arcfour128', 'arcfour')
1217  );
1218  }
1219 
1220  if (class_exists('\phpseclib\Crypt\RC4') === false) {
1221  $encryption_algorithms = array_diff(
1222  $encryption_algorithms,
1223  array('arcfour256', 'arcfour128', 'arcfour')
1224  );
1225  }
1226  if (class_exists('\phpseclib\Crypt\Rijndael') === false) {
1227  $encryption_algorithms = array_diff(
1228  $encryption_algorithms,
1229  array('aes128-ctr', 'aes192-ctr', 'aes256-ctr', 'aes128-cbc', 'aes192-cbc', 'aes256-cbc')
1230  );
1231  }
1232  if (class_exists('\phpseclib\Crypt\Twofish') === false) {
1233  $encryption_algorithms = array_diff(
1234  $encryption_algorithms,
1235  array('twofish128-ctr', 'twofish192-ctr', 'twofish256-ctr', 'twofish128-cbc', 'twofish192-cbc', 'twofish256-cbc', 'twofish-cbc')
1236  );
1237  }
1238  if (class_exists('\phpseclib\Crypt\Blowfish') === false) {
1239  $encryption_algorithms = array_diff(
1240  $encryption_algorithms,
1241  array('blowfish-ctr', 'blowfish-cbc')
1242  );
1243  }
1244  if (class_exists('\phpseclib\Crypt\TripleDES') === false) {
1245  $encryption_algorithms = array_diff(
1246  $encryption_algorithms,
1247  array('3des-ctr', '3des-cbc')
1248  );
1249  }
1250  $encryption_algorithms = array_values($encryption_algorithms);
1251 
1252  $mac_algorithms = array(
1253  // from <http://www.ietf.org/rfc/rfc6668.txt>:
1254  'hmac-sha2-256',// RECOMMENDED HMAC-SHA256 (digest length = key length = 32)
1255 
1256  'hmac-sha1-96', // RECOMMENDED first 96 bits of HMAC-SHA1 (digest length = 12, key length = 20)
1257  'hmac-sha1', // REQUIRED HMAC-SHA1 (digest length = key length = 20)
1258  'hmac-md5-96', // OPTIONAL first 96 bits of HMAC-MD5 (digest length = 12, key length = 16)
1259  'hmac-md5', // OPTIONAL HMAC-MD5 (digest length = key length = 16)
1260  //'none' // OPTIONAL no MAC; NOT RECOMMENDED
1261  );
1262 
1263  $compression_algorithms = array(
1264  'none' // REQUIRED no compression
1265  //'zlib' // OPTIONAL ZLIB (LZ77) compression
1266  );
1267 
1268  // some SSH servers have buggy implementations of some of the above algorithms
1269  switch ($this->server_identifier) {
1270  case 'SSH-2.0-SSHD':
1271  $mac_algorithms = array_values(array_diff(
1272  $mac_algorithms,
1273  array('hmac-sha1-96', 'hmac-md5-96')
1274  ));
1275  }
1276 
1277  $str_kex_algorithms = implode(',', $kex_algorithms);
1278  $str_server_host_key_algorithms = implode(',', $server_host_key_algorithms);
1279  $encryption_algorithms_server_to_client = $encryption_algorithms_client_to_server = implode(',', $encryption_algorithms);
1280  $mac_algorithms_server_to_client = $mac_algorithms_client_to_server = implode(',', $mac_algorithms);
1281  $compression_algorithms_server_to_client = $compression_algorithms_client_to_server = implode(',', $compression_algorithms);
1282 
1283  $client_cookie = Random::string(16);
1284 
1285  $response = $kexinit_payload_server;
1286  $this->_string_shift($response, 1); // skip past the message number (it should be SSH_MSG_KEXINIT)
1287  $server_cookie = $this->_string_shift($response, 16);
1288 
1289  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1290  $this->kex_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1291 
1292  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1293  $this->server_host_key_algorithms = explode(',', $this->_string_shift($response, $temp['length']));
1294 
1295  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1296  $this->encryption_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1297 
1298  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1299  $this->encryption_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1300 
1301  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1302  $this->mac_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1303 
1304  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1305  $this->mac_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1306 
1307  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1308  $this->compression_algorithms_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1309 
1310  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1311  $this->compression_algorithms_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1312 
1313  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1314  $this->languages_client_to_server = explode(',', $this->_string_shift($response, $temp['length']));
1315 
1316  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1317  $this->languages_server_to_client = explode(',', $this->_string_shift($response, $temp['length']));
1318 
1319  extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1320  $first_kex_packet_follows = $first_kex_packet_follows != 0;
1321 
1322  // the sending of SSH2_MSG_KEXINIT could go in one of two places. this is the second place.
1323  $kexinit_payload_client = pack(
1324  'Ca*Na*Na*Na*Na*Na*Na*Na*Na*Na*Na*CN',
1325  NET_SSH2_MSG_KEXINIT,
1326  $client_cookie,
1327  strlen($str_kex_algorithms),
1328  $str_kex_algorithms,
1329  strlen($str_server_host_key_algorithms),
1330  $str_server_host_key_algorithms,
1331  strlen($encryption_algorithms_client_to_server),
1332  $encryption_algorithms_client_to_server,
1333  strlen($encryption_algorithms_server_to_client),
1334  $encryption_algorithms_server_to_client,
1335  strlen($mac_algorithms_client_to_server),
1336  $mac_algorithms_client_to_server,
1337  strlen($mac_algorithms_server_to_client),
1338  $mac_algorithms_server_to_client,
1339  strlen($compression_algorithms_client_to_server),
1340  $compression_algorithms_client_to_server,
1341  strlen($compression_algorithms_server_to_client),
1342  $compression_algorithms_server_to_client,
1343  0,
1344  '',
1345  0,
1346  '',
1347  0,
1348  0
1349  );
1350 
1351  if (!$this->_send_binary_packet($kexinit_payload_client)) {
1352  return false;
1353  }
1354  // here ends the second place.
1355 
1356  // we need to decide upon the symmetric encryption algorithms before we do the diffie-hellman key exchange
1357  // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the
1358  // diffie-hellman key exchange as fast as possible
1359  $decrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_server_to_client);
1360  $decryptKeyLength = $this->_encryption_algorithm_to_key_size($decrypt);
1361  if ($decryptKeyLength === null) {
1362  user_error('No compatible server to client encryption algorithms found');
1363  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1364  }
1365 
1366  $encrypt = $this->_array_intersect_first($encryption_algorithms, $this->encryption_algorithms_client_to_server);
1367  $encryptKeyLength = $this->_encryption_algorithm_to_key_size($encrypt);
1368  if ($encryptKeyLength === null) {
1369  user_error('No compatible client to server encryption algorithms found');
1370  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1371  }
1372 
1373  // through diffie-hellman key exchange a symmetric key is obtained
1374  $kex_algorithm = $this->_array_intersect_first($kex_algorithms, $this->kex_algorithms);
1375  if ($kex_algorithm === false) {
1376  user_error('No compatible key exchange algorithms found');
1377  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1378  }
1379 
1380  // Only relevant in diffie-hellman-group-exchange-sha{1,256}, otherwise empty.
1381  $exchange_hash_rfc4419 = '';
1382 
1383  if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
1384  $x = Random::string(32);
1385  $eBytes = \Sodium::crypto_box_publickey_from_secretkey($x);
1386  $clientKexInitMessage = NET_SSH2_MSG_KEX_ECDH_INIT;
1387  $serverKexReplyMessage = NET_SSH2_MSG_KEX_ECDH_REPLY;
1388  $kexHash = new Hash('sha256');
1389  } else {
1390  if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1391  $dh_group_sizes_packed = pack(
1392  'NNN',
1393  $this->kex_dh_group_size_min,
1394  $this->kex_dh_group_size_preferred,
1395  $this->kex_dh_group_size_max
1396  );
1397  $packet = pack(
1398  'Ca*',
1399  NET_SSH2_MSG_KEXDH_GEX_REQUEST,
1400  $dh_group_sizes_packed
1401  );
1402  if (!$this->_send_binary_packet($packet)) {
1403  return false;
1404  }
1405 
1406  $response = $this->_get_binary_packet();
1407  if ($response === false) {
1408  user_error('Connection closed by server');
1409  return false;
1410  }
1411  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1412  if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) {
1413  user_error('Expected SSH_MSG_KEX_DH_GEX_GROUP');
1414  return false;
1415  }
1416 
1417  extract(unpack('NprimeLength', $this->_string_shift($response, 4)));
1418  $primeBytes = $this->_string_shift($response, $primeLength);
1419  $prime = new BigInteger($primeBytes, -256);
1420 
1421  extract(unpack('NgLength', $this->_string_shift($response, 4)));
1422  $gBytes = $this->_string_shift($response, $gLength);
1423  $g = new BigInteger($gBytes, -256);
1424 
1425  $exchange_hash_rfc4419 = pack(
1426  'a*Na*Na*',
1427  $dh_group_sizes_packed,
1428  $primeLength,
1429  $primeBytes,
1430  $gLength,
1431  $gBytes
1432  );
1433 
1434  $clientKexInitMessage = NET_SSH2_MSG_KEXDH_GEX_INIT;
1435  $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_GEX_REPLY;
1436  } else {
1437  switch ($kex_algorithm) {
1438  // see http://tools.ietf.org/html/rfc2409#section-6.2 and
1439  // http://tools.ietf.org/html/rfc2412, appendex E
1440  case 'diffie-hellman-group1-sha1':
1441  $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1442  '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1443  '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1444  'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
1445  break;
1446  // see http://tools.ietf.org/html/rfc3526#section-3
1447  case 'diffie-hellman-group14-sha1':
1448  $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
1449  '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
1450  '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
1451  'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
1452  '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
1453  '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
1454  'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
1455  '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
1456  break;
1457  }
1458  // For both diffie-hellman-group1-sha1 and diffie-hellman-group14-sha1
1459  // the generator field element is 2 (decimal) and the hash function is sha1.
1460  $g = new BigInteger(2);
1461  $prime = new BigInteger($prime, 16);
1462  $clientKexInitMessage = NET_SSH2_MSG_KEXDH_INIT;
1463  $serverKexReplyMessage = NET_SSH2_MSG_KEXDH_REPLY;
1464  }
1465 
1466  switch ($kex_algorithm) {
1467  case 'diffie-hellman-group-exchange-sha256':
1468  $kexHash = new Hash('sha256');
1469  break;
1470  default:
1471  $kexHash = new Hash('sha1');
1472  }
1473 
1474  /* To increase the speed of the key exchange, both client and server may
1475  reduce the size of their private exponents. It should be at least
1476  twice as long as the key material that is generated from the shared
1477  secret. For more details, see the paper by van Oorschot and Wiener
1478  [VAN-OORSCHOT].
1479 
1480  -- http://tools.ietf.org/html/rfc4419#section-6.2 */
1481  $one = new BigInteger(1);
1482  $keyLength = min($kexHash->getLength(), max($encryptKeyLength, $decryptKeyLength));
1483  $max = $one->bitwise_leftShift(16 * $keyLength); // 2 * 8 * $keyLength
1484  $max = $max->subtract($one);
1485 
1486  $x = $one->random($one, $max);
1487  $e = $g->modPow($x, $prime);
1488 
1489  $eBytes = $e->toBytes(true);
1490  }
1491  $data = pack('CNa*', $clientKexInitMessage, strlen($eBytes), $eBytes);
1492 
1493  if (!$this->_send_binary_packet($data)) {
1494  user_error('Connection closed by server');
1495  return false;
1496  }
1497 
1498  $response = $this->_get_binary_packet();
1499  if ($response === false) {
1500  user_error('Connection closed by server');
1501  return false;
1502  }
1503  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1504 
1505  if ($type != $serverKexReplyMessage) {
1506  user_error('Expected SSH_MSG_KEXDH_REPLY');
1507  return false;
1508  }
1509 
1510  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1511  $this->server_public_host_key = $server_public_host_key = $this->_string_shift($response, $temp['length']);
1512 
1513  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
1514  $public_key_format = $this->_string_shift($server_public_host_key, $temp['length']);
1515 
1516  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1517  $fBytes = $this->_string_shift($response, $temp['length']);
1518 
1519  $temp = unpack('Nlength', $this->_string_shift($response, 4));
1520  $this->signature = $this->_string_shift($response, $temp['length']);
1521 
1522  $temp = unpack('Nlength', $this->_string_shift($this->signature, 4));
1523  $this->signature_format = $this->_string_shift($this->signature, $temp['length']);
1524 
1525  if ($kex_algorithm === 'curve25519-sha256@libssh.org') {
1526  if (strlen($fBytes) !== 32) {
1527  user_error('Received curve25519 public key of invalid length.');
1528  return false;
1529  }
1530  $key = new BigInteger(\Sodium::crypto_scalarmult($x, $fBytes), 256);
1531  \Sodium::sodium_memzero($x);
1532  } else {
1533  $f = new BigInteger($fBytes, -256);
1534  $key = $f->modPow($x, $prime);
1535  }
1536  $keyBytes = $key->toBytes(true);
1537 
1538  $this->exchange_hash = pack(
1539  'Na*Na*Na*Na*Na*a*Na*Na*Na*',
1540  strlen($this->identifier),
1541  $this->identifier,
1542  strlen($this->server_identifier),
1543  $this->server_identifier,
1544  strlen($kexinit_payload_client),
1545  $kexinit_payload_client,
1546  strlen($kexinit_payload_server),
1547  $kexinit_payload_server,
1548  strlen($this->server_public_host_key),
1549  $this->server_public_host_key,
1550  $exchange_hash_rfc4419,
1551  strlen($eBytes),
1552  $eBytes,
1553  strlen($fBytes),
1554  $fBytes,
1555  strlen($keyBytes),
1556  $keyBytes
1557  );
1558 
1559  $this->exchange_hash = $kexHash->hash($this->exchange_hash);
1560 
1561  if ($this->session_id === false) {
1562  $this->session_id = $this->exchange_hash;
1563  }
1564 
1565  $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1566  if ($server_host_key_algorithm === false) {
1567  user_error('No compatible server host key algorithms found');
1568  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1569  }
1570 
1571  if ($public_key_format != $server_host_key_algorithm || $this->signature_format != $server_host_key_algorithm) {
1572  user_error('Server Host Key Algorithm Mismatch');
1573  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1574  }
1575 
1576  $packet = pack(
1577  'C',
1578  NET_SSH2_MSG_NEWKEYS
1579  );
1580 
1581  if (!$this->_send_binary_packet($packet)) {
1582  return false;
1583  }
1584 
1585  $response = $this->_get_binary_packet();
1586 
1587  if ($response === false) {
1588  user_error('Connection closed by server');
1589  return false;
1590  }
1591 
1592  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1593 
1594  if ($type != NET_SSH2_MSG_NEWKEYS) {
1595  user_error('Expected SSH_MSG_NEWKEYS');
1596  return false;
1597  }
1598 
1599  $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes);
1600 
1601  $this->encrypt = $this->_encryption_algorithm_to_crypt_instance($encrypt);
1602  if ($this->encrypt) {
1603  if ($this->crypto_engine) {
1604  $this->encrypt->setEngine($this->crypto_engine);
1605  }
1606  if ($this->encrypt->block_size) {
1607  $this->encrypt_block_size = $this->encrypt->block_size;
1608  }
1609  $this->encrypt->enableContinuousBuffer();
1610  $this->encrypt->disablePadding();
1611 
1612  $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'A' . $this->session_id);
1613  while ($this->encrypt_block_size > strlen($iv)) {
1614  $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1615  }
1616  $this->encrypt->setIV(substr($iv, 0, $this->encrypt_block_size));
1617 
1618  $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'C' . $this->session_id);
1619  while ($encryptKeyLength > strlen($key)) {
1620  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1621  }
1622  $this->encrypt->setKey(substr($key, 0, $encryptKeyLength));
1623  }
1624 
1625  $this->decrypt = $this->_encryption_algorithm_to_crypt_instance($decrypt);
1626  if ($this->decrypt) {
1627  if ($this->crypto_engine) {
1628  $this->decrypt->setEngine($this->crypto_engine);
1629  }
1630  if ($this->decrypt->block_size) {
1631  $this->decrypt_block_size = $this->decrypt->block_size;
1632  }
1633  $this->decrypt->enableContinuousBuffer();
1634  $this->decrypt->disablePadding();
1635 
1636  $iv = $kexHash->hash($keyBytes . $this->exchange_hash . 'B' . $this->session_id);
1637  while ($this->decrypt_block_size > strlen($iv)) {
1638  $iv.= $kexHash->hash($keyBytes . $this->exchange_hash . $iv);
1639  }
1640  $this->decrypt->setIV(substr($iv, 0, $this->decrypt_block_size));
1641 
1642  $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'D' . $this->session_id);
1643  while ($decryptKeyLength > strlen($key)) {
1644  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1645  }
1646  $this->decrypt->setKey(substr($key, 0, $decryptKeyLength));
1647  }
1648 
1649  /* The "arcfour128" algorithm is the RC4 cipher, as described in
1650  [SCHNEIER], using a 128-bit key. The first 1536 bytes of keystream
1651  generated by the cipher MUST be discarded, and the first byte of the
1652  first encrypted packet MUST be encrypted using the 1537th byte of
1653  keystream.
1654 
1655  -- http://tools.ietf.org/html/rfc4345#section-4 */
1656  if ($encrypt == 'arcfour128' || $encrypt == 'arcfour256') {
1657  $this->encrypt->encrypt(str_repeat("\0", 1536));
1658  }
1659  if ($decrypt == 'arcfour128' || $decrypt == 'arcfour256') {
1660  $this->decrypt->decrypt(str_repeat("\0", 1536));
1661  }
1662 
1663  $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_client_to_server);
1664  if ($mac_algorithm === false) {
1665  user_error('No compatible client to server message authentication algorithms found');
1666  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1667  }
1668 
1669  $createKeyLength = 0; // ie. $mac_algorithm == 'none'
1670  switch ($mac_algorithm) {
1671  case 'hmac-sha2-256':
1672  $this->hmac_create = new Hash('sha256');
1673  $createKeyLength = 32;
1674  break;
1675  case 'hmac-sha1':
1676  $this->hmac_create = new Hash('sha1');
1677  $createKeyLength = 20;
1678  break;
1679  case 'hmac-sha1-96':
1680  $this->hmac_create = new Hash('sha1-96');
1681  $createKeyLength = 20;
1682  break;
1683  case 'hmac-md5':
1684  $this->hmac_create = new Hash('md5');
1685  $createKeyLength = 16;
1686  break;
1687  case 'hmac-md5-96':
1688  $this->hmac_create = new Hash('md5-96');
1689  $createKeyLength = 16;
1690  }
1691 
1692  $mac_algorithm = $this->_array_intersect_first($mac_algorithms, $this->mac_algorithms_server_to_client);
1693  if ($mac_algorithm === false) {
1694  user_error('No compatible server to client message authentication algorithms found');
1695  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1696  }
1697 
1698  $checkKeyLength = 0;
1699  $this->hmac_size = 0;
1700  switch ($mac_algorithm) {
1701  case 'hmac-sha2-256':
1702  $this->hmac_check = new Hash('sha256');
1703  $checkKeyLength = 32;
1704  $this->hmac_size = 32;
1705  break;
1706  case 'hmac-sha1':
1707  $this->hmac_check = new Hash('sha1');
1708  $checkKeyLength = 20;
1709  $this->hmac_size = 20;
1710  break;
1711  case 'hmac-sha1-96':
1712  $this->hmac_check = new Hash('sha1-96');
1713  $checkKeyLength = 20;
1714  $this->hmac_size = 12;
1715  break;
1716  case 'hmac-md5':
1717  $this->hmac_check = new Hash('md5');
1718  $checkKeyLength = 16;
1719  $this->hmac_size = 16;
1720  break;
1721  case 'hmac-md5-96':
1722  $this->hmac_check = new Hash('md5-96');
1723  $checkKeyLength = 16;
1724  $this->hmac_size = 12;
1725  }
1726 
1727  $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
1728  while ($createKeyLength > strlen($key)) {
1729  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1730  }
1731  $this->hmac_create->setKey(substr($key, 0, $createKeyLength));
1732 
1733  $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'F' . $this->session_id);
1734  while ($checkKeyLength > strlen($key)) {
1735  $key.= $kexHash->hash($keyBytes . $this->exchange_hash . $key);
1736  }
1737  $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1738 
1739  $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_server_to_client);
1740  if ($compression_algorithm === false) {
1741  user_error('No compatible server to client compression algorithms found');
1742  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1743  }
1744  $this->decompress = $compression_algorithm == 'zlib';
1745 
1746  $compression_algorithm = $this->_array_intersect_first($compression_algorithms, $this->compression_algorithms_client_to_server);
1747  if ($compression_algorithm === false) {
1748  user_error('No compatible client to server compression algorithms found');
1749  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1750  }
1751  $this->compress = $compression_algorithm == 'zlib';
1752 
1753  return true;
1754  }
1755 
1764  {
1765  switch ($algorithm) {
1766  case 'none':
1767  return 0;
1768  case 'aes128-cbc':
1769  case 'aes128-ctr':
1770  case 'arcfour':
1771  case 'arcfour128':
1772  case 'blowfish-cbc':
1773  case 'blowfish-ctr':
1774  case 'twofish128-cbc':
1775  case 'twofish128-ctr':
1776  return 16;
1777  case '3des-cbc':
1778  case '3des-ctr':
1779  case 'aes192-cbc':
1780  case 'aes192-ctr':
1781  case 'twofish192-cbc':
1782  case 'twofish192-ctr':
1783  return 24;
1784  case 'aes256-cbc':
1785  case 'aes256-ctr':
1786  case 'arcfour256':
1787  case 'twofish-cbc':
1788  case 'twofish256-cbc':
1789  case 'twofish256-ctr':
1790  return 32;
1791  }
1792  return null;
1793  }
1794 
1804  {
1805  switch ($algorithm) {
1806  case '3des-cbc':
1807  return new TripleDES();
1808  case '3des-ctr':
1809  return new TripleDES(Base::MODE_CTR);
1810  case 'aes256-cbc':
1811  case 'aes192-cbc':
1812  case 'aes128-cbc':
1813  return new Rijndael();
1814  case 'aes256-ctr':
1815  case 'aes192-ctr':
1816  case 'aes128-ctr':
1817  return new Rijndael(Base::MODE_CTR);
1818  case 'blowfish-cbc':
1819  return new Blowfish();
1820  case 'blowfish-ctr':
1821  return new Blowfish(Base::MODE_CTR);
1822  case 'twofish128-cbc':
1823  case 'twofish192-cbc':
1824  case 'twofish256-cbc':
1825  case 'twofish-cbc':
1826  return new Twofish();
1827  case 'twofish128-ctr':
1828  case 'twofish192-ctr':
1829  case 'twofish256-ctr':
1830  return new Twofish(Base::MODE_CTR);
1831  case 'arcfour':
1832  case 'arcfour128':
1833  case 'arcfour256':
1834  return new RC4();
1835  }
1836  return null;
1837  }
1838 
1851  function login($username)
1852  {
1853  $args = func_get_args();
1854  return call_user_func_array(array(&$this, '_login'), $args);
1855  }
1856 
1867  function _login($username)
1868  {
1869  if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
1870  if (!$this->_connect()) {
1871  return false;
1872  }
1873  }
1874 
1875  $args = array_slice(func_get_args(), 1);
1876  if (empty($args)) {
1877  return $this->_login_helper($username);
1878  }
1879 
1880  foreach ($args as $arg) {
1881  if ($this->_login_helper($username, $arg)) {
1882  return true;
1883  }
1884  }
1885  return false;
1886  }
1887 
1898  function _login_helper($username, $password = null)
1899  {
1900  if (!($this->bitmap & self::MASK_CONNECTED)) {
1901  return false;
1902  }
1903 
1904  if (!($this->bitmap & self::MASK_LOGIN_REQ)) {
1905  $packet = pack(
1906  'CNa*',
1907  NET_SSH2_MSG_SERVICE_REQUEST,
1908  strlen('ssh-userauth'),
1909  'ssh-userauth'
1910  );
1911 
1912  if (!$this->_send_binary_packet($packet)) {
1913  return false;
1914  }
1915 
1916  $response = $this->_get_binary_packet();
1917  if ($response === false) {
1918  user_error('Connection closed by server');
1919  return false;
1920  }
1921 
1922  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1923 
1924  if ($type != NET_SSH2_MSG_SERVICE_ACCEPT) {
1925  user_error('Expected SSH_MSG_SERVICE_ACCEPT');
1926  return false;
1927  }
1928  $this->bitmap |= self::MASK_LOGIN_REQ;
1929  }
1930 
1931  if (strlen($this->last_interactive_response)) {
1932  return !is_string($password) && !is_array($password) ? false : $this->_keyboard_interactive_process($password);
1933  }
1934 
1935  if ($password instanceof RSA) {
1936  return $this->_privatekey_login($username, $password);
1937  } elseif ($password instanceof Agent) {
1938  return $this->_ssh_agent_login($username, $password);
1939  }
1940 
1941  if (is_array($password)) {
1942  if ($this->_keyboard_interactive_login($username, $password)) {
1943  $this->bitmap |= self::MASK_LOGIN;
1944  return true;
1945  }
1946  return false;
1947  }
1948 
1949  if (!isset($password)) {
1950  $packet = pack(
1951  'CNa*Na*Na*',
1952  NET_SSH2_MSG_USERAUTH_REQUEST,
1953  strlen($username),
1954  $username,
1955  strlen('ssh-connection'),
1956  'ssh-connection',
1957  strlen('none'),
1958  'none'
1959  );
1960 
1961  if (!$this->_send_binary_packet($packet)) {
1962  return false;
1963  }
1964 
1965  $response = $this->_get_binary_packet();
1966  if ($response === false) {
1967  user_error('Connection closed by server');
1968  return false;
1969  }
1970 
1971  extract(unpack('Ctype', $this->_string_shift($response, 1)));
1972 
1973  switch ($type) {
1974  case NET_SSH2_MSG_USERAUTH_SUCCESS:
1975  $this->bitmap |= self::MASK_LOGIN;
1976  return true;
1977  //case NET_SSH2_MSG_USERAUTH_FAILURE:
1978  default:
1979  return false;
1980  }
1981  }
1982 
1983  $packet = pack(
1984  'CNa*Na*Na*CNa*',
1985  NET_SSH2_MSG_USERAUTH_REQUEST,
1986  strlen($username),
1987  $username,
1988  strlen('ssh-connection'),
1989  'ssh-connection',
1990  strlen('password'),
1991  'password',
1992  0,
1993  strlen($password),
1994  $password
1995  );
1996 
1997  // remove the username and password from the logged packet
1998  if (!defined('NET_SSH2_LOGGING')) {
1999  $logged = null;
2000  } else {
2001  $logged = pack(
2002  'CNa*Na*Na*CNa*',
2003  NET_SSH2_MSG_USERAUTH_REQUEST,
2004  strlen('username'),
2005  'username',
2006  strlen('ssh-connection'),
2007  'ssh-connection',
2008  strlen('password'),
2009  'password',
2010  0,
2011  strlen('password'),
2012  'password'
2013  );
2014  }
2015 
2016  if (!$this->_send_binary_packet($packet, $logged)) {
2017  return false;
2018  }
2019 
2020  $response = $this->_get_binary_packet();
2021  if ($response === false) {
2022  user_error('Connection closed by server');
2023  return false;
2024  }
2025 
2026  extract(unpack('Ctype', $this->_string_shift($response, 1)));
2027 
2028  switch ($type) {
2029  case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed
2030  if (defined('NET_SSH2_LOGGING')) {
2031  $this->message_number_log[count($this->message_number_log) - 1] = 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ';
2032  }
2033  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2034  $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . utf8_decode($this->_string_shift($response, $length));
2035  return $this->_disconnect(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
2036  case NET_SSH2_MSG_USERAUTH_FAILURE:
2037  // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees
2038  // multi-factor authentication
2039  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2040  $auth_methods = explode(',', $this->_string_shift($response, $length));
2041  extract(unpack('Cpartial_success', $this->_string_shift($response, 1)));
2042  $partial_success = $partial_success != 0;
2043 
2044  if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) {
2045  if ($this->_keyboard_interactive_login($username, $password)) {
2046  $this->bitmap |= self::MASK_LOGIN;
2047  return true;
2048  }
2049  return false;
2050  }
2051  return false;
2052  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2053  $this->bitmap |= self::MASK_LOGIN;
2054  return true;
2055  }
2056 
2057  return false;
2058  }
2059 
2071  {
2072  $packet = pack(
2073  'CNa*Na*Na*Na*Na*',
2074  NET_SSH2_MSG_USERAUTH_REQUEST,
2075  strlen($username),
2076  $username,
2077  strlen('ssh-connection'),
2078  'ssh-connection',
2079  strlen('keyboard-interactive'),
2080  'keyboard-interactive',
2081  0,
2082  '',
2083  0,
2084  ''
2085  );
2086 
2087  if (!$this->_send_binary_packet($packet)) {
2088  return false;
2089  }
2090 
2092  }
2093 
2102  {
2103  $responses = func_get_args();
2104 
2105  if (strlen($this->last_interactive_response)) {
2107  } else {
2108  $orig = $response = $this->_get_binary_packet();
2109  if ($response === false) {
2110  user_error('Connection closed by server');
2111  return false;
2112  }
2113  }
2114 
2115  extract(unpack('Ctype', $this->_string_shift($response, 1)));
2116 
2117  switch ($type) {
2118  case NET_SSH2_MSG_USERAUTH_INFO_REQUEST:
2119  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2120  $this->_string_shift($response, $length); // name; may be empty
2121  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2122  $this->_string_shift($response, $length); // instruction; may be empty
2123  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2124  $this->_string_shift($response, $length); // language tag; may be empty
2125  extract(unpack('Nnum_prompts', $this->_string_shift($response, 4)));
2126 
2127  for ($i = 0; $i < count($responses); $i++) {
2128  if (is_array($responses[$i])) {
2129  foreach ($responses[$i] as $key => $value) {
2130  $this->keyboard_requests_responses[$key] = $value;
2131  }
2132  unset($responses[$i]);
2133  }
2134  }
2135  $responses = array_values($responses);
2136 
2137  if (isset($this->keyboard_requests_responses)) {
2138  for ($i = 0; $i < $num_prompts; $i++) {
2139  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2140  // prompt - ie. "Password: "; must not be empty
2141  $prompt = $this->_string_shift($response, $length);
2142  //$echo = $this->_string_shift($response) != chr(0);
2143  foreach ($this->keyboard_requests_responses as $key => $value) {
2144  if (substr($prompt, 0, strlen($key)) == $key) {
2145  $responses[] = $value;
2146  break;
2147  }
2148  }
2149  }
2150  }
2151 
2152  // see http://tools.ietf.org/html/rfc4256#section-3.2
2153  if (strlen($this->last_interactive_response)) {
2154  $this->last_interactive_response = '';
2155  } elseif (defined('NET_SSH2_LOGGING')) {
2156  $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2157  'UNKNOWN',
2158  'NET_SSH2_MSG_USERAUTH_INFO_REQUEST',
2159  $this->message_number_log[count($this->message_number_log) - 1]
2160  );
2161  }
2162 
2163  if (!count($responses) && $num_prompts) {
2164  $this->last_interactive_response = $orig;
2165  return false;
2166  }
2167 
2168  /*
2169  After obtaining the requested information from the user, the client
2170  MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message.
2171  */
2172  // see http://tools.ietf.org/html/rfc4256#section-3.4
2173  $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses));
2174  for ($i = 0; $i < count($responses); $i++) {
2175  $packet.= pack('Na*', strlen($responses[$i]), $responses[$i]);
2176  $logged.= pack('Na*', strlen('dummy-answer'), 'dummy-answer');
2177  }
2178 
2179  if (!$this->_send_binary_packet($packet, $logged)) {
2180  return false;
2181  }
2182 
2183  if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
2184  $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2185  'UNKNOWN',
2186  'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE',
2187  $this->message_number_log[count($this->message_number_log) - 1]
2188  );
2189  }
2190 
2191  /*
2192  After receiving the response, the server MUST send either an
2193  SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another
2194  SSH_MSG_USERAUTH_INFO_REQUEST message.
2195  */
2196  // maybe phpseclib should force close the connection after x request / responses? unless something like that is done
2197  // there could be an infinite loop of request / responses.
2198  return $this->_keyboard_interactive_process();
2199  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2200  return true;
2201  case NET_SSH2_MSG_USERAUTH_FAILURE:
2202  return false;
2203  }
2204 
2205  return false;
2206  }
2207 
2216  function _ssh_agent_login($username, $agent)
2217  {
2218  $this->agent = $agent;
2219  $keys = $agent->requestIdentities();
2220  foreach ($keys as $key) {
2221  if ($this->_privatekey_login($username, $key)) {
2222  return true;
2223  }
2224  }
2225 
2226  return false;
2227  }
2228 
2239  function _privatekey_login($username, $privatekey)
2240  {
2241  // see http://tools.ietf.org/html/rfc4253#page-15
2242  $publickey = $privatekey->getPublicKey(RSA::PUBLIC_FORMAT_RAW);
2243  if ($publickey === false) {
2244  return false;
2245  }
2246 
2247  $publickey = array(
2248  'e' => $publickey['e']->toBytes(true),
2249  'n' => $publickey['n']->toBytes(true)
2250  );
2251  $publickey = pack(
2252  'Na*Na*Na*',
2253  strlen('ssh-rsa'),
2254  'ssh-rsa',
2255  strlen($publickey['e']),
2256  $publickey['e'],
2257  strlen($publickey['n']),
2258  $publickey['n']
2259  );
2260 
2261  $part1 = pack(
2262  'CNa*Na*Na*',
2263  NET_SSH2_MSG_USERAUTH_REQUEST,
2264  strlen($username),
2265  $username,
2266  strlen('ssh-connection'),
2267  'ssh-connection',
2268  strlen('publickey'),
2269  'publickey'
2270  );
2271  $part2 = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publickey), $publickey);
2272 
2273  $packet = $part1 . chr(0) . $part2;
2274  if (!$this->_send_binary_packet($packet)) {
2275  return false;
2276  }
2277 
2278  $response = $this->_get_binary_packet();
2279  if ($response === false) {
2280  user_error('Connection closed by server');
2281  return false;
2282  }
2283 
2284  extract(unpack('Ctype', $this->_string_shift($response, 1)));
2285 
2286  switch ($type) {
2287  case NET_SSH2_MSG_USERAUTH_FAILURE:
2288  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2289  $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
2290  return false;
2291  case NET_SSH2_MSG_USERAUTH_PK_OK:
2292  // we'll just take it on faith that the public key blob and the public key algorithm name are as
2293  // they should be
2294  if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) {
2295  $this->message_number_log[count($this->message_number_log) - 1] = str_replace(
2296  'UNKNOWN',
2297  'NET_SSH2_MSG_USERAUTH_PK_OK',
2298  $this->message_number_log[count($this->message_number_log) - 1]
2299  );
2300  }
2301  }
2302 
2303  $packet = $part1 . chr(1) . $part2;
2304  $privatekey->setSignatureMode(RSA::SIGNATURE_PKCS1);
2305  $signature = $privatekey->sign(pack('Na*a*', strlen($this->session_id), $this->session_id, $packet));
2306  $signature = pack('Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($signature), $signature);
2307  $packet.= pack('Na*', strlen($signature), $signature);
2308 
2309  if (!$this->_send_binary_packet($packet)) {
2310  return false;
2311  }
2312 
2313  $response = $this->_get_binary_packet();
2314  if ($response === false) {
2315  user_error('Connection closed by server');
2316  return false;
2317  }
2318 
2319  extract(unpack('Ctype', $this->_string_shift($response, 1)));
2320 
2321  switch ($type) {
2322  case NET_SSH2_MSG_USERAUTH_FAILURE:
2323  // either the login is bad or the server employs multi-factor authentication
2324  return false;
2325  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2326  $this->bitmap |= self::MASK_LOGIN;
2327  return true;
2328  }
2329 
2330  return false;
2331  }
2332 
2342  function setTimeout($timeout)
2343  {
2344  $this->timeout = $this->curTimeout = $timeout;
2345  }
2346 
2352  function getStdError()
2353  {
2354  return $this->stdErrorLog;
2355  }
2356 
2368  function exec($command, $callback = null)
2369  {
2370  $this->curTimeout = $this->timeout;
2371  $this->is_timeout = false;
2372  $this->stdErrorLog = '';
2373 
2374  if (!($this->bitmap & self::MASK_LOGIN)) {
2375  return false;
2376  }
2377 
2378  // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
2379  // be adjusted". 0x7FFFFFFF is, at 2GB, the max size. technically, it should probably be decremented, but,
2380  // honestly, if you're transfering more than 2GB, you probably shouldn't be using phpseclib, anyway.
2381  // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
2382  $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
2383  // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
2384  // uses 0x4000, that's what will be used here, as well.
2385  $packet_size = 0x4000;
2386 
2387  $packet = pack(
2388  'CNa*N3',
2389  NET_SSH2_MSG_CHANNEL_OPEN,
2390  strlen('session'),
2391  'session',
2392  self::CHANNEL_EXEC,
2393  $this->window_size_server_to_client[self::CHANNEL_EXEC],
2394  $packet_size
2395  );
2396 
2397  if (!$this->_send_binary_packet($packet)) {
2398  return false;
2399  }
2400 
2401  $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
2402 
2403  $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2404  if ($response === false) {
2405  return false;
2406  }
2407 
2408  if ($this->request_pty === true) {
2409  $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2410  $packet = pack(
2411  'CNNa*CNa*N5a*',
2412  NET_SSH2_MSG_CHANNEL_REQUEST,
2413  $this->server_channels[self::CHANNEL_EXEC],
2414  strlen('pty-req'),
2415  'pty-req',
2416  1,
2417  strlen('vt100'),
2418  'vt100',
2419  $this->windowColumns,
2420  $this->windowRows,
2421  0,
2422  0,
2423  strlen($terminal_modes),
2424  $terminal_modes
2425  );
2426 
2427  if (!$this->_send_binary_packet($packet)) {
2428  return false;
2429  }
2430 
2431  $response = $this->_get_binary_packet();
2432  if ($response === false) {
2433  user_error('Connection closed by server');
2434  return false;
2435  }
2436 
2437  list(, $type) = unpack('C', $this->_string_shift($response, 1));
2438 
2439  switch ($type) {
2440  case NET_SSH2_MSG_CHANNEL_SUCCESS:
2441  break;
2442  case NET_SSH2_MSG_CHANNEL_FAILURE:
2443  default:
2444  user_error('Unable to request pseudo-terminal');
2445  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2446  }
2447  $this->in_request_pty_exec = true;
2448  }
2449 
2450  // sending a pty-req SSH_MSG_CHANNEL_REQUEST message is unnecessary and, in fact, in most cases, slows things
2451  // down. the one place where it might be desirable is if you're doing something like \phpseclib\Net\SSH2::exec('ping localhost &').
2452  // with a pty-req SSH_MSG_CHANNEL_REQUEST, exec() will return immediately and the ping process will then
2453  // then immediately terminate. without such a request exec() will loop indefinitely. the ping process won't end but
2454  // neither will your script.
2455 
2456  // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by
2457  // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the
2458  // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates.
2459  $packet = pack(
2460  'CNNa*CNa*',
2461  NET_SSH2_MSG_CHANNEL_REQUEST,
2462  $this->server_channels[self::CHANNEL_EXEC],
2463  strlen('exec'),
2464  'exec',
2465  1,
2466  strlen($command),
2467  $command
2468  );
2469  if (!$this->_send_binary_packet($packet)) {
2470  return false;
2471  }
2472 
2473  $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
2474 
2475  $response = $this->_get_channel_packet(self::CHANNEL_EXEC);
2476  if ($response === false) {
2477  return false;
2478  }
2479 
2480  $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA;
2481 
2482  if ($callback === false || $this->in_request_pty_exec) {
2483  return true;
2484  }
2485 
2486  $output = '';
2487  while (true) {
2488  $temp = $this->_get_channel_packet(self::CHANNEL_EXEC);
2489  switch (true) {
2490  case $temp === true:
2491  return is_callable($callback) ? true : $output;
2492  case $temp === false:
2493  return false;
2494  default:
2495  if (is_callable($callback)) {
2496  if (call_user_func($callback, $temp) === true) {
2497  $this->_close_channel(self::CHANNEL_EXEC);
2498  return true;
2499  }
2500  } else {
2501  $output.= $temp;
2502  }
2503  }
2504  }
2505  }
2506 
2515  function _initShell()
2516  {
2517  if ($this->in_request_pty_exec === true) {
2518  return true;
2519  }
2520 
2521  $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
2522  $packet_size = 0x4000;
2523 
2524  $packet = pack(
2525  'CNa*N3',
2526  NET_SSH2_MSG_CHANNEL_OPEN,
2527  strlen('session'),
2528  'session',
2529  self::CHANNEL_SHELL,
2530  $this->window_size_server_to_client[self::CHANNEL_SHELL],
2531  $packet_size
2532  );
2533 
2534  if (!$this->_send_binary_packet($packet)) {
2535  return false;
2536  }
2537 
2538  $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
2539 
2540  $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
2541  if ($response === false) {
2542  return false;
2543  }
2544 
2545  $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2546  $packet = pack(
2547  'CNNa*CNa*N5a*',
2548  NET_SSH2_MSG_CHANNEL_REQUEST,
2549  $this->server_channels[self::CHANNEL_SHELL],
2550  strlen('pty-req'),
2551  'pty-req',
2552  1,
2553  strlen('vt100'),
2554  'vt100',
2555  $this->windowColumns,
2556  $this->windowRows,
2557  0,
2558  0,
2559  strlen($terminal_modes),
2560  $terminal_modes
2561  );
2562 
2563  if (!$this->_send_binary_packet($packet)) {
2564  return false;
2565  }
2566 
2567  $response = $this->_get_binary_packet();
2568  if ($response === false) {
2569  user_error('Connection closed by server');
2570  return false;
2571  }
2572 
2573  list(, $type) = unpack('C', $this->_string_shift($response, 1));
2574 
2575  switch ($type) {
2576  case NET_SSH2_MSG_CHANNEL_SUCCESS:
2577  // if a pty can't be opened maybe commands can still be executed
2578  case NET_SSH2_MSG_CHANNEL_FAILURE:
2579  break;
2580  default:
2581  user_error('Unable to request pseudo-terminal');
2582  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2583  }
2584 
2585  $packet = pack(
2586  'CNNa*C',
2587  NET_SSH2_MSG_CHANNEL_REQUEST,
2588  $this->server_channels[self::CHANNEL_SHELL],
2589  strlen('shell'),
2590  'shell',
2591  1
2592  );
2593  if (!$this->_send_binary_packet($packet)) {
2594  return false;
2595  }
2596 
2597  $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
2598 
2599  $response = $this->_get_channel_packet(self::CHANNEL_SHELL);
2600  if ($response === false) {
2601  return false;
2602  }
2603 
2604  $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA;
2605 
2606  $this->bitmap |= self::MASK_SHELL;
2607 
2608  return true;
2609  }
2610 
2620  {
2621  switch (true) {
2622  case $this->in_subsystem:
2623  return self::CHANNEL_SUBSYSTEM;
2624  case $this->in_request_pty_exec:
2625  return self::CHANNEL_EXEC;
2626  default:
2627  return self::CHANNEL_SHELL;
2628  }
2629  }
2630 
2638  {
2639  $channel = self::CHANNEL_EXEC;
2640  do {
2641  if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) {
2642  return $channel;
2643  }
2644  } while ($channel++ < self::CHANNEL_SUBSYSTEM);
2645 
2646  return false;
2647  }
2648 
2661  function read($expect = '', $mode = self::READ_SIMPLE)
2662  {
2663  $this->curTimeout = $this->timeout;
2664  $this->is_timeout = false;
2665 
2666  if (!($this->bitmap & self::MASK_LOGIN)) {
2667  user_error('Operation disallowed prior to login()');
2668  return false;
2669  }
2670 
2671  if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
2672  user_error('Unable to initiate an interactive shell session');
2673  return false;
2674  }
2675 
2676  $channel = $this->_get_interactive_channel();
2677 
2678  $match = $expect;
2679  while (true) {
2680  if ($mode == self::READ_REGEX) {
2681  preg_match($expect, substr($this->interactiveBuffer, -1024), $matches);
2682  $match = isset($matches[0]) ? $matches[0] : '';
2683  }
2684  $pos = strlen($match) ? strpos($this->interactiveBuffer, $match) : false;
2685  if ($pos !== false) {
2686  return $this->_string_shift($this->interactiveBuffer, $pos + strlen($match));
2687  }
2688  $response = $this->_get_channel_packet($channel);
2689  if (is_bool($response)) {
2690  $this->in_request_pty_exec = false;
2691  return $response ? $this->_string_shift($this->interactiveBuffer, strlen($this->interactiveBuffer)) : false;
2692  }
2693 
2694  $this->interactiveBuffer.= $response;
2695  }
2696  }
2697 
2706  function write($cmd)
2707  {
2708  if (!($this->bitmap & self::MASK_LOGIN)) {
2709  user_error('Operation disallowed prior to login()');
2710  return false;
2711  }
2712 
2713  if (!($this->bitmap & self::MASK_SHELL) && !$this->_initShell()) {
2714  user_error('Unable to initiate an interactive shell session');
2715  return false;
2716  }
2717 
2718  return $this->_send_channel_packet($this->_get_interactive_channel(), $cmd);
2719  }
2720 
2735  function startSubsystem($subsystem)
2736  {
2737  $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
2738 
2739  $packet = pack(
2740  'CNa*N3',
2741  NET_SSH2_MSG_CHANNEL_OPEN,
2742  strlen('session'),
2743  'session',
2744  self::CHANNEL_SUBSYSTEM,
2745  $this->window_size,
2746  0x4000
2747  );
2748 
2749  if (!$this->_send_binary_packet($packet)) {
2750  return false;
2751  }
2752 
2753  $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
2754 
2755  $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
2756  if ($response === false) {
2757  return false;
2758  }
2759 
2760  $packet = pack(
2761  'CNNa*CNa*',
2762  NET_SSH2_MSG_CHANNEL_REQUEST,
2763  $this->server_channels[self::CHANNEL_SUBSYSTEM],
2764  strlen('subsystem'),
2765  'subsystem',
2766  1,
2767  strlen($subsystem),
2768  $subsystem
2769  );
2770  if (!$this->_send_binary_packet($packet)) {
2771  return false;
2772  }
2773 
2774  $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST;
2775 
2776  $response = $this->_get_channel_packet(self::CHANNEL_SUBSYSTEM);
2777 
2778  if ($response === false) {
2779  return false;
2780  }
2781 
2782  $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA;
2783 
2784  $this->bitmap |= self::MASK_SHELL;
2785  $this->in_subsystem = true;
2786 
2787  return true;
2788  }
2789 
2797  function stopSubsystem()
2798  {
2799  $this->in_subsystem = false;
2800  $this->_close_channel(self::CHANNEL_SUBSYSTEM);
2801  return true;
2802  }
2803 
2811  function reset()
2812  {
2813  $this->_close_channel($this->_get_interactive_channel());
2814  }
2815 
2823  function isTimeout()
2824  {
2825  return $this->is_timeout;
2826  }
2827 
2833  function disconnect()
2834  {
2835  $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2836  if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) {
2837  fclose($this->realtime_log_file);
2838  }
2839  }
2840 
2849  function __destruct()
2850  {
2851  $this->disconnect();
2852  }
2853 
2860  function isConnected()
2861  {
2862  return (bool) ($this->bitmap & self::MASK_CONNECTED);
2863  }
2864 
2871  function isAuthenticated()
2872  {
2873  return (bool) ($this->bitmap & self::MASK_LOGIN);
2874  }
2875 
2886  {
2887  if (!is_resource($this->fsock) || feof($this->fsock)) {
2888  user_error('Connection closed prematurely');
2889  $this->bitmap = 0;
2890  return false;
2891  }
2892 
2893  $start = microtime(true);
2894  $raw = fread($this->fsock, $this->decrypt_block_size);
2895 
2896  if (!strlen($raw)) {
2897  return '';
2898  }
2899 
2900  if ($this->decrypt !== false) {
2901  $raw = $this->decrypt->decrypt($raw);
2902  }
2903  if ($raw === false) {
2904  user_error('Unable to decrypt content');
2905  return false;
2906  }
2907 
2908  extract(unpack('Npacket_length/Cpadding_length', $this->_string_shift($raw, 5)));
2909 
2910  $remaining_length = $packet_length + 4 - $this->decrypt_block_size;
2911 
2912  // quoting <http://tools.ietf.org/html/rfc4253#section-6.1>,
2913  // "implementations SHOULD check that the packet length is reasonable"
2914  // PuTTY uses 0x9000 as the actual max packet size and so to shall we
2915  if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) {
2916  user_error('Invalid size');
2917  return false;
2918  }
2919 
2920  $buffer = '';
2921  while ($remaining_length > 0) {
2922  $temp = fread($this->fsock, $remaining_length);
2923  if ($temp === false || feof($this->fsock)) {
2924  user_error('Error reading from socket');
2925  $this->bitmap = 0;
2926  return false;
2927  }
2928  $buffer.= $temp;
2929  $remaining_length-= strlen($temp);
2930  }
2931  $stop = microtime(true);
2932  if (strlen($buffer)) {
2933  $raw.= $this->decrypt !== false ? $this->decrypt->decrypt($buffer) : $buffer;
2934  }
2935 
2936  $payload = $this->_string_shift($raw, $packet_length - $padding_length - 1);
2937  $padding = $this->_string_shift($raw, $padding_length); // should leave $raw empty
2938 
2939  if ($this->hmac_check !== false) {
2940  $hmac = fread($this->fsock, $this->hmac_size);
2941  if ($hmac === false || strlen($hmac) != $this->hmac_size) {
2942  user_error('Error reading socket');
2943  $this->bitmap = 0;
2944  return false;
2945  } elseif ($hmac != $this->hmac_check->hash(pack('NNCa*', $this->get_seq_no, $packet_length, $padding_length, $payload . $padding))) {
2946  user_error('Invalid HMAC');
2947  return false;
2948  }
2949  }
2950 
2951  //if ($this->decompress) {
2952  // $payload = gzinflate(substr($payload, 2));
2953  //}
2954 
2955  $this->get_seq_no++;
2956 
2957  if (defined('NET_SSH2_LOGGING')) {
2958  $current = microtime(true);
2959  $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')';
2960  $message_number = '<- ' . $message_number .
2961  ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
2962  $this->_append_log($message_number, $payload);
2963  $this->last_packet = $current;
2964  }
2965 
2966  return $this->_filter($payload);
2967  }
2968 
2978  function _filter($payload)
2979  {
2980  switch (ord($payload[0])) {
2981  case NET_SSH2_MSG_DISCONNECT:
2982  $this->_string_shift($payload, 1);
2983  extract(unpack('Nreason_code/Nlength', $this->_string_shift($payload, 8)));
2984  $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n" . utf8_decode($this->_string_shift($payload, $length));
2985  $this->bitmap = 0;
2986  return false;
2987  case NET_SSH2_MSG_IGNORE:
2988  $payload = $this->_get_binary_packet();
2989  break;
2990  case NET_SSH2_MSG_DEBUG:
2991  $this->_string_shift($payload, 2);
2992  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
2993  $this->errors[] = 'SSH_MSG_DEBUG: ' . utf8_decode($this->_string_shift($payload, $length));
2994  $payload = $this->_get_binary_packet();
2995  break;
2996  case NET_SSH2_MSG_UNIMPLEMENTED:
2997  return false;
2998  case NET_SSH2_MSG_KEXINIT:
2999  if ($this->session_id !== false) {
3000  if (!$this->_key_exchange($payload)) {
3001  $this->bitmap = 0;
3002  return false;
3003  }
3004  $payload = $this->_get_binary_packet();
3005  }
3006  }
3007 
3008  // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in
3009  if (($this->bitmap & self::MASK_CONNECTED) && !($this->bitmap & self::MASK_LOGIN) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) {
3010  $this->_string_shift($payload, 1);
3011  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3012  $this->banner_message = utf8_decode($this->_string_shift($payload, $length));
3013  $payload = $this->_get_binary_packet();
3014  }
3015 
3016  // only called when we've already logged in
3017  if (($this->bitmap & self::MASK_CONNECTED) && ($this->bitmap & self::MASK_LOGIN)) {
3018  switch (ord($payload[0])) {
3019  case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4
3020  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3021  $this->errors[] = 'SSH_MSG_GLOBAL_REQUEST: ' . $this->_string_shift($payload, $length);
3022 
3023  if (!$this->_send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE))) {
3024  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3025  }
3026 
3027  $payload = $this->_get_binary_packet();
3028  break;
3029  case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1
3030  $this->_string_shift($payload, 1);
3031  extract(unpack('Nlength', $this->_string_shift($payload, 4)));
3032  $data = $this->_string_shift($payload, $length);
3033  extract(unpack('Nserver_channel', $this->_string_shift($payload, 4)));
3034  switch ($data) {
3035  case 'auth-agent':
3036  case 'auth-agent@openssh.com':
3037  if (isset($this->agent)) {
3038  $new_channel = self::CHANNEL_AGENT_FORWARD;
3039 
3040  extract(unpack('Nremote_window_size', $this->_string_shift($payload, 4)));
3041  extract(unpack('Nremote_maximum_packet_size', $this->_string_shift($payload, 4)));
3042 
3043  $this->packet_size_client_to_server[$new_channel] = $remote_window_size;
3044  $this->window_size_server_to_client[$new_channel] = $remote_maximum_packet_size;
3045  $this->window_size_client_to_server[$new_channel] = $this->window_size;
3046 
3047  $packet_size = 0x4000;
3048 
3049  $packet = pack(
3050  'CN4',
3051  NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,
3052  $server_channel,
3053  $new_channel,
3054  $packet_size,
3055  $packet_size
3056  );
3057 
3058  $this->server_channels[$new_channel] = $server_channel;
3059  $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION;
3060  if (!$this->_send_binary_packet($packet)) {
3061  return false;
3062  }
3063  }
3064  break;
3065  default:
3066  $packet = pack(
3067  'CN3a*Na*',
3068  NET_SSH2_MSG_REQUEST_FAILURE,
3069  $server_channel,
3070  NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
3071  0,
3072  '',
3073  0,
3074  ''
3075  );
3076 
3077  if (!$this->_send_binary_packet($packet)) {
3078  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3079  }
3080  }
3081  $payload = $this->_get_binary_packet();
3082  break;
3083  case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3084  $this->_string_shift($payload, 1);
3085  extract(unpack('Nchannel', $this->_string_shift($payload, 4)));
3086  extract(unpack('Nwindow_size', $this->_string_shift($payload, 4)));
3087  $this->window_size_client_to_server[$channel]+= $window_size;
3088 
3089  $payload = ($this->bitmap & self::MASK_WINDOW_ADJUST) ? true : $this->_get_binary_packet();
3090  }
3091  }
3092 
3093  return $payload;
3094  }
3095 
3103  function enableQuietMode()
3104  {
3105  $this->quiet_mode = true;
3106  }
3107 
3115  function disableQuietMode()
3116  {
3117  $this->quiet_mode = false;
3118  }
3119 
3129  {
3130  return $this->quiet_mode;
3131  }
3132 
3138  function enablePTY()
3139  {
3140  $this->request_pty = true;
3141  }
3142 
3148  function disablePTY()
3149  {
3150  $this->request_pty = false;
3151  }
3152 
3161  function isPTYEnabled()
3162  {
3163  return $this->request_pty;
3164  }
3165 
3175  function _get_channel_packet($client_channel, $skip_extended = false)
3176  {
3177  if (!empty($this->channel_buffers[$client_channel])) {
3178  return array_shift($this->channel_buffers[$client_channel]);
3179  }
3180 
3181  while (true) {
3182  if ($this->curTimeout) {
3183  if ($this->curTimeout < 0) {
3184  $this->is_timeout = true;
3185  return true;
3186  }
3187 
3188  $read = array($this->fsock);
3189  $write = $except = null;
3190 
3191  $start = microtime(true);
3192  $sec = floor($this->curTimeout);
3193  $usec = 1000000 * ($this->curTimeout - $sec);
3194  // on windows this returns a "Warning: Invalid CRT parameters detected" error
3195  if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3196  $this->is_timeout = true;
3197  return true;
3198  }
3199  $elapsed = microtime(true) - $start;
3200  $this->curTimeout-= $elapsed;
3201  }
3202 
3203  $response = $this->_get_binary_packet();
3204  if ($response === false) {
3205  user_error('Connection closed by server');
3206  return false;
3207  }
3208  if ($client_channel == -1 && $response === true) {
3209  return true;
3210  }
3211  if (!strlen($response)) {
3212  return '';
3213  }
3214 
3215  extract(unpack('Ctype', $this->_string_shift($response, 1)));
3216 
3217  if ($type == NET_SSH2_MSG_CHANNEL_OPEN) {
3218  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3219  } else {
3220  extract(unpack('Nchannel', $this->_string_shift($response, 4)));
3221  }
3222 
3223  // will not be setup yet on incoming channel open request
3224  if (isset($channel) && isset($this->channel_status[$channel]) && isset($this->window_size_server_to_client[$channel])) {
3225  $this->window_size_server_to_client[$channel]-= strlen($response);
3226 
3227  // resize the window, if appropriate
3228  if ($this->window_size_server_to_client[$channel] < 0) {
3229  $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_size);
3230  if (!$this->_send_binary_packet($packet)) {
3231  return false;
3232  }
3233  $this->window_size_server_to_client[$channel]+= $this->window_size;
3234  }
3235 
3236  switch ($this->channel_status[$channel]) {
3237  case NET_SSH2_MSG_CHANNEL_OPEN:
3238  switch ($type) {
3239  case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3240  extract(unpack('Nserver_channel', $this->_string_shift($response, 4)));
3241  $this->server_channels[$channel] = $server_channel;
3242  extract(unpack('Nwindow_size', $this->_string_shift($response, 4)));
3243  if ($window_size < 0) {
3244  $window_size&= 0x7FFFFFFF;
3245  $window_size+= 0x80000000;
3246  }
3247  $this->window_size_client_to_server[$channel] = $window_size;
3248  $temp = unpack('Npacket_size_client_to_server', $this->_string_shift($response, 4));
3249  $this->packet_size_client_to_server[$channel] = $temp['packet_size_client_to_server'];
3250  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3251  $this->_on_channel_open();
3252  return $result;
3253  //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3254  default:
3255  user_error('Unable to open channel');
3256  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3257  }
3258  break;
3259  case NET_SSH2_MSG_CHANNEL_REQUEST:
3260  switch ($type) {
3261  case NET_SSH2_MSG_CHANNEL_SUCCESS:
3262  return true;
3263  case NET_SSH2_MSG_CHANNEL_FAILURE:
3264  return false;
3265  default:
3266  user_error('Unable to fulfill channel request');
3267  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3268  }
3269  case NET_SSH2_MSG_CHANNEL_CLOSE:
3270  return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3271  }
3272  }
3273 
3274  // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA
3275 
3276  switch ($type) {
3277  case NET_SSH2_MSG_CHANNEL_DATA:
3278  /*
3279  if ($channel == self::CHANNEL_EXEC) {
3280  // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server
3281  // this actually seems to make things twice as fast. more to the point, the message right after
3282  // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise.
3283  // in OpenSSH it slows things down but only by a couple thousandths of a second.
3284  $this->_send_channel_packet($channel, chr(0));
3285  }
3286  */
3287  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3288  $data = $this->_string_shift($response, $length);
3289 
3290  if ($channel == self::CHANNEL_AGENT_FORWARD) {
3291  $agent_response = $this->agent->_forward_data($data);
3292  if (!is_bool($agent_response)) {
3293  $this->_send_channel_packet($channel, $agent_response);
3294  }
3295  break;
3296  }
3297 
3298  if ($client_channel == $channel) {
3299  return $data;
3300  }
3301  if (!isset($this->channel_buffers[$channel])) {
3302  $this->channel_buffers[$channel] = array();
3303  }
3304  $this->channel_buffers[$channel][] = $data;
3305  break;
3306  case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3307  /*
3308  if ($client_channel == self::CHANNEL_EXEC) {
3309  $this->_send_channel_packet($client_channel, chr(0));
3310  }
3311  */
3312  // currently, there's only one possible value for $data_type_code: NET_SSH2_EXTENDED_DATA_STDERR
3313  extract(unpack('Ndata_type_code/Nlength', $this->_string_shift($response, 8)));
3314  $data = $this->_string_shift($response, $length);
3315  $this->stdErrorLog.= $data;
3316  if ($skip_extended || $this->quiet_mode) {
3317  break;
3318  }
3319  if ($client_channel == $channel) {
3320  return $data;
3321  }
3322  if (!isset($this->channel_buffers[$channel])) {
3323  $this->channel_buffers[$channel] = array();
3324  }
3325  $this->channel_buffers[$channel][] = $data;
3326  break;
3327  case NET_SSH2_MSG_CHANNEL_REQUEST:
3328  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3329  $value = $this->_string_shift($response, $length);
3330  switch ($value) {
3331  case 'exit-signal':
3332  $this->_string_shift($response, 1);
3333  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3334  $this->errors[] = 'SSH_MSG_CHANNEL_REQUEST (exit-signal): ' . $this->_string_shift($response, $length);
3335  $this->_string_shift($response, 1);
3336  extract(unpack('Nlength', $this->_string_shift($response, 4)));
3337  if ($length) {
3338  $this->errors[count($this->errors)].= "\r\n" . $this->_string_shift($response, $length);
3339  }
3340 
3341  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3342  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3343 
3344  $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF;
3345 
3346  break;
3347  case 'exit-status':
3348  extract(unpack('Cfalse/Nexit_status', $this->_string_shift($response, 5)));
3349  $this->exit_status = $exit_status;
3350 
3351  // "The client MAY ignore these messages."
3352  // -- http://tools.ietf.org/html/rfc4254#section-6.10
3353 
3354  break;
3355  default:
3356  // "Some systems may not implement signals, in which case they SHOULD ignore this message."
3357  // -- http://tools.ietf.org/html/rfc4254#section-6.9
3358  break;
3359  }
3360  break;
3361  case NET_SSH2_MSG_CHANNEL_CLOSE:
3362  $this->curTimeout = 0;
3363 
3364  if ($this->bitmap & self::MASK_SHELL) {
3365  $this->bitmap&= ~self::MASK_SHELL;
3366  }
3367  if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
3368  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
3369  }
3370 
3371  $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3372  return true;
3373  case NET_SSH2_MSG_CHANNEL_EOF:
3374  break;
3375  default:
3376  user_error('Error reading channel data');
3377  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3378  }
3379  }
3380  }
3381 
3393  function _send_binary_packet($data, $logged = null)
3394  {
3395  if (!is_resource($this->fsock) || feof($this->fsock)) {
3396  user_error('Connection closed prematurely');
3397  $this->bitmap = 0;
3398  return false;
3399  }
3400 
3401  //if ($this->compress) {
3402  // // the -4 removes the checksum:
3403  // // http://php.net/function.gzcompress#57710
3404  // $data = substr(gzcompress($data), 0, -4);
3405  //}
3406 
3407  // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
3408  $packet_length = strlen($data) + 9;
3409  // round up to the nearest $this->encrypt_block_size
3410  $packet_length+= (($this->encrypt_block_size - 1) * $packet_length) % $this->encrypt_block_size;
3411  // subtracting strlen($data) is obvious - subtracting 5 is necessary because of packet_length and padding_length
3412  $padding_length = $packet_length - strlen($data) - 5;
3413  $padding = Random::string($padding_length);
3414 
3415  // we subtract 4 from packet_length because the packet_length field isn't supposed to include itself
3416  $packet = pack('NCa*', $packet_length - 4, $padding_length, $data . $padding);
3417 
3418  $hmac = $this->hmac_create !== false ? $this->hmac_create->hash(pack('Na*', $this->send_seq_no, $packet)) : '';
3419  $this->send_seq_no++;
3420 
3421  if ($this->encrypt !== false) {
3422  $packet = $this->encrypt->encrypt($packet);
3423  }
3424 
3425  $packet.= $hmac;
3426 
3427  $start = microtime(true);
3428  $result = strlen($packet) == fputs($this->fsock, $packet);
3429  $stop = microtime(true);
3430 
3431  if (defined('NET_SSH2_LOGGING')) {
3432  $current = microtime(true);
3433  $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
3434  $message_number = '-> ' . $message_number .
3435  ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3436  $this->_append_log($message_number, isset($logged) ? $logged : $data);
3437  $this->last_packet = $current;
3438  }
3439 
3440  return $result;
3441  }
3442 
3451  function _append_log($message_number, $message)
3452  {
3453  // remove the byte identifying the message type from all but the first two messages (ie. the identification strings)
3454  if (strlen($message_number) > 2) {
3455  $this->_string_shift($message);
3456  }
3457 
3458  switch (NET_SSH2_LOGGING) {
3459  // useful for benchmarks
3460  case self::LOG_SIMPLE:
3461  $this->message_number_log[] = $message_number;
3462  break;
3463  // the most useful log for SSH2
3464  case self::LOG_COMPLEX:
3465  $this->message_number_log[] = $message_number;
3466  $this->log_size+= strlen($message);
3467  $this->message_log[] = $message;
3468  while ($this->log_size > self::LOG_MAX_SIZE) {
3469  $this->log_size-= strlen(array_shift($this->message_log));
3470  array_shift($this->message_number_log);
3471  }
3472  break;
3473  // dump the output out realtime; packets may be interspersed with non packets,
3474  // passwords won't be filtered out and select other packets may not be correctly
3475  // identified
3476  case self::LOG_REALTIME:
3477  switch (PHP_SAPI) {
3478  case 'cli':
3479  $start = $stop = "\r\n";
3480  break;
3481  default:
3482  $start = '<pre>';
3483  $stop = '</pre>';
3484  }
3485  echo $start . $this->_format_log(array($message), array($message_number)) . $stop;
3486  @flush();
3487  @ob_flush();
3488  break;
3489  // basically the same thing as self::LOG_REALTIME with the caveat that self::LOG_REALTIME_FILE
3490  // needs to be defined and that the resultant log file will be capped out at self::LOG_MAX_SIZE.
3491  // the earliest part of the log file is denoted by the first <<< START >>> and is not going to necessarily
3492  // at the beginning of the file
3493  case self::LOG_REALTIME_FILE:
3494  if (!isset($this->realtime_log_file)) {
3495  // PHP doesn't seem to like using constants in fopen()
3496  $filename = self::LOG_REALTIME_FILENAME;
3497  $fp = fopen($filename, 'w');
3498  $this->realtime_log_file = $fp;
3499  }
3500  if (!is_resource($this->realtime_log_file)) {
3501  break;
3502  }
3503  $entry = $this->_format_log(array($message), array($message_number));
3504  if ($this->realtime_log_wrap) {
3505  $temp = "<<< START >>>\r\n";
3506  $entry.= $temp;
3507  fseek($this->realtime_log_file, ftell($this->realtime_log_file) - strlen($temp));
3508  }
3509  $this->realtime_log_size+= strlen($entry);
3510  if ($this->realtime_log_size > self::LOG_MAX_SIZE) {
3511  fseek($this->realtime_log_file, 0);
3512  $this->realtime_log_size = strlen($entry);
3513  $this->realtime_log_wrap = true;
3514  }
3515  fputs($this->realtime_log_file, $entry);
3516  }
3517  }
3518 
3529  function _send_channel_packet($client_channel, $data)
3530  {
3531  while (strlen($data)) {
3532  if (!$this->window_size_client_to_server[$client_channel]) {
3533  $this->bitmap^= self::MASK_WINDOW_ADJUST;
3534  // using an invalid channel will let the buffers be built up for the valid channels
3535  $this->_get_channel_packet(-1);
3536  $this->bitmap^= self::MASK_WINDOW_ADJUST;
3537  }
3538 
3539  /* The maximum amount of data allowed is determined by the maximum
3540  packet size for the channel, and the current window size, whichever
3541  is smaller.
3542  -- http://tools.ietf.org/html/rfc4254#section-5.2 */
3543  $max_size = min(
3544  $this->packet_size_client_to_server[$client_channel],
3545  $this->window_size_client_to_server[$client_channel]
3546  );
3547 
3548  $temp = $this->_string_shift($data, $max_size);
3549  $packet = pack(
3550  'CN2a*',
3551  NET_SSH2_MSG_CHANNEL_DATA,
3552  $this->server_channels[$client_channel],
3553  strlen($temp),
3554  $temp
3555  );
3556  $this->window_size_client_to_server[$client_channel]-= strlen($temp);
3557  if (!$this->_send_binary_packet($packet)) {
3558  return false;
3559  }
3560  }
3561 
3562  return true;
3563  }
3564 
3577  function _close_channel($client_channel, $want_reply = false)
3578  {
3579  // see http://tools.ietf.org/html/rfc4254#section-5.3
3580 
3581  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel]));
3582 
3583  if (!$want_reply) {
3584  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
3585  }
3586 
3587  $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
3588 
3589  $this->curTimeout = 0;
3590 
3591  while (!is_bool($this->_get_channel_packet($client_channel))) {
3592  }
3593 
3594  if ($want_reply) {
3595  $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
3596  }
3597 
3598  if ($this->bitmap & self::MASK_SHELL) {
3599  $this->bitmap&= ~self::MASK_SHELL;
3600  }
3601  }
3602 
3610  function _disconnect($reason)
3611  {
3612  if ($this->bitmap & self::MASK_CONNECTED) {
3613  $data = pack('CNNa*Na*', NET_SSH2_MSG_DISCONNECT, $reason, 0, '', 0, '');
3614  $this->_send_binary_packet($data);
3615  $this->bitmap = 0;
3616  fclose($this->fsock);
3617  return false;
3618  }
3619  }
3620 
3631  function _string_shift(&$string, $index = 1)
3632  {
3633  $substr = substr($string, 0, $index);
3634  $string = substr($string, $index);
3635  return $substr;
3636  }
3637 
3648  function _define_array()
3649  {
3650  $args = func_get_args();
3651  foreach ($args as $arg) {
3652  foreach ($arg as $key => $value) {
3653  if (!defined($value)) {
3654  define($value, $key);
3655  } else {
3656  break 2;
3657  }
3658  }
3659  }
3660  }
3661 
3670  function getLog()
3671  {
3672  if (!defined('NET_SSH2_LOGGING')) {
3673  return false;
3674  }
3675 
3676  switch (NET_SSH2_LOGGING) {
3677  case self::LOG_SIMPLE:
3679  break;
3680  case self::LOG_COMPLEX:
3681  return $this->_format_log($this->message_log, $this->message_number_log);
3682  break;
3683  default:
3684  return false;
3685  }
3686  }
3687 
3696  function _format_log($message_log, $message_number_log)
3697  {
3698  $output = '';
3699  for ($i = 0; $i < count($message_log); $i++) {
3700  $output.= $message_number_log[$i] . "\r\n";
3701  $current_log = $message_log[$i];
3702  $j = 0;
3703  do {
3704  if (strlen($current_log)) {
3705  $output.= str_pad(dechex($j), 7, '0', STR_PAD_LEFT) . '0 ';
3706  }
3707  $fragment = $this->_string_shift($current_log, $this->log_short_width);
3708  $hex = substr(preg_replace_callback('#.#s', array($this, '_format_log_helper'), $fragment), strlen($this->log_boundary));
3709  // replace non ASCII printable characters with dots
3710  // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters
3711  // also replace < with a . since < messes up the output on web browsers
3712  $raw = preg_replace('#[^\x20-\x7E]|<#', '.', $fragment);
3713  $output.= str_pad($hex, $this->log_long_width - $this->log_short_width, ' ') . $raw . "\r\n";
3714  $j++;
3715  } while (strlen($current_log));
3716  $output.= "\r\n";
3717  }
3718 
3719  return $output;
3720  }
3721 
3731  function _format_log_helper($matches)
3732  {
3733  return $this->log_boundary . str_pad(dechex(ord($matches[0])), 2, '0', STR_PAD_LEFT);
3734  }
3735 
3745  function _on_channel_open()
3746  {
3747  if (isset($this->agent)) {
3748  $this->agent->_on_channel_open($this);
3749  }
3750  }
3751 
3761  function _array_intersect_first($array1, $array2)
3762  {
3763  foreach ($array1 as $value) {
3764  if (in_array($value, $array2)) {
3765  return $value;
3766  }
3767  }
3768  return false;
3769  }
3770 
3777  function getErrors()
3778  {
3779  return $this->errors;
3780  }
3781 
3788  function getLastError()
3789  {
3790  $count = count($this->errors);
3791 
3792  if ($count > 0) {
3793  return $this->errors[$count - 1];
3794  }
3795  }
3796 
3804  {
3805  $this->_connect();
3806 
3807  return $this->server_identifier;
3808  }
3809 
3816  function getKexAlgorithms()
3817  {
3818  $this->_connect();
3819 
3820  return $this->kex_algorithms;
3821  }
3822 
3830  {
3831  $this->_connect();
3832 
3834  }
3835 
3843  {
3844  $this->_connect();
3845 
3847  }
3848 
3856  {
3857  $this->_connect();
3858 
3860  }
3861 
3869  {
3870  $this->_connect();
3871 
3873  }
3874 
3882  {
3883  $this->_connect();
3884 
3886  }
3887 
3895  {
3896  $this->_connect();
3897 
3899  }
3900 
3908  {
3909  $this->_connect();
3910 
3912  }
3913 
3921  {
3922  $this->_connect();
3923 
3925  }
3926 
3934  {
3935  $this->_connect();
3936 
3938  }
3939 
3949  function getBannerMessage()
3950  {
3951  return $this->banner_message;
3952  }
3953 
3964  {
3965  if (!($this->bitmap & self::MASK_CONSTRUCTOR)) {
3966  if (!$this->_connect()) {
3967  return false;
3968  }
3969  }
3970 
3971  $signature = $this->signature;
3972  $server_public_host_key = $this->server_public_host_key;
3973 
3974  extract(unpack('Nlength', $this->_string_shift($server_public_host_key, 4)));
3975  $this->_string_shift($server_public_host_key, $length);
3976 
3977  if ($this->signature_validated) {
3978  return $this->bitmap ?
3979  $this->signature_format . ' ' . base64_encode($this->server_public_host_key) :
3980  false;
3981  }
3982 
3983  $this->signature_validated = true;
3984 
3985  switch ($this->signature_format) {
3986  case 'ssh-dss':
3987  $zero = new BigInteger();
3988 
3989  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3990  $p = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3991 
3992  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3993  $q = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3994 
3995  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3996  $g = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
3997 
3998  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
3999  $y = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
4000 
4001  /* The value for 'dss_signature_blob' is encoded as a string containing
4002  r, followed by s (which are 160-bit integers, without lengths or
4003  padding, unsigned, and in network byte order). */
4004  $temp = unpack('Nlength', $this->_string_shift($signature, 4));
4005  if ($temp['length'] != 40) {
4006  user_error('Invalid signature');
4007  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
4008  }
4009 
4010  $r = new BigInteger($this->_string_shift($signature, 20), 256);
4011  $s = new BigInteger($this->_string_shift($signature, 20), 256);
4012 
4013  switch (true) {
4014  case $r->equals($zero):
4015  case $r->compare($q) >= 0:
4016  case $s->equals($zero):
4017  case $s->compare($q) >= 0:
4018  user_error('Invalid signature');
4019  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
4020  }
4021 
4022  $w = $s->modInverse($q);
4023 
4024  $u1 = $w->multiply(new BigInteger(sha1($this->exchange_hash), 16));
4025  list(, $u1) = $u1->divide($q);
4026 
4027  $u2 = $w->multiply($r);
4028  list(, $u2) = $u2->divide($q);
4029 
4030  $g = $g->modPow($u1, $p);
4031  $y = $y->modPow($u2, $p);
4032 
4033  $v = $g->multiply($y);
4034  list(, $v) = $v->divide($p);
4035  list(, $v) = $v->divide($q);
4036 
4037  if (!$v->equals($r)) {
4038  user_error('Bad server signature');
4039  return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
4040  }
4041 
4042  break;
4043  case 'ssh-rsa':
4044  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
4045  $e = new BigInteger($this->_string_shift($server_public_host_key, $temp['length']), -256);
4046 
4047  $temp = unpack('Nlength', $this->_string_shift($server_public_host_key, 4));
4048  $rawN = $this->_string_shift($server_public_host_key, $temp['length']);
4049  $n = new BigInteger($rawN, -256);
4050  $nLength = strlen(ltrim($rawN, "\0"));
4051 
4052  /*
4053  $temp = unpack('Nlength', $this->_string_shift($signature, 4));
4054  $signature = $this->_string_shift($signature, $temp['length']);
4055 
4056  $rsa = new RSA();
4057  $rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
4058  $rsa->loadKey(array('e' => $e, 'n' => $n), RSA::PUBLIC_FORMAT_RAW);
4059  if (!$rsa->verify($this->exchange_hash, $signature)) {
4060  user_error('Bad server signature');
4061  return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
4062  }
4063  */
4064 
4065  $temp = unpack('Nlength', $this->_string_shift($signature, 4));
4066  $s = new BigInteger($this->_string_shift($signature, $temp['length']), 256);
4067 
4068  // validate an RSA signature per "8.2 RSASSA-PKCS1-v1_5", "5.2.2 RSAVP1", and "9.1 EMSA-PSS" in the
4069  // following URL:
4070  // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.pdf
4071 
4072  // also, see SSHRSA.c (rsa2_verifysig) in PuTTy's source.
4073 
4074  if ($s->compare(new BigInteger()) < 0 || $s->compare($n->subtract(new BigInteger(1))) > 0) {
4075  user_error('Invalid signature');
4076  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
4077  }
4078 
4079  $s = $s->modPow($e, $n);
4080  $s = $s->toBytes();
4081 
4082  $h = pack('N4H*', 0x00302130, 0x0906052B, 0x0E03021A, 0x05000414, sha1($this->exchange_hash));
4083  $h = chr(0x01) . str_repeat(chr(0xFF), $nLength - 2 - strlen($h)) . $h;
4084 
4085  if ($s != $h) {
4086  user_error('Bad server signature');
4087  return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
4088  }
4089  break;
4090  default:
4091  user_error('Unsupported signature format');
4092  return $this->_disconnect(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE);
4093  }
4094 
4095  return $this->signature_format . ' ' . base64_encode($this->server_public_host_key);
4096  }
4097 
4104  function getExitStatus()
4105  {
4106  if (is_null($this->exit_status)) {
4107  return false;
4108  }
4109  return $this->exit_status;
4110  }
4111 
4118  function getWindowColumns()
4119  {
4120  return $this->windowColumns;
4121  }
4122 
4129  function getWindowRows()
4130  {
4131  return $this->windowRows;
4132  }
4133 
4140  function setWindowColumns($value)
4141  {
4142  $this->windowColumns = $value;
4143  }
4144 
4151  function setWindowRows($value)
4152  {
4153  $this->windowRows = $value;
4154  }
4155 
4163  function setWindowSize($columns = 80, $rows = 24)
4164  {
4165  $this->windowColumns = $columns;
4166  $this->windowRows = $rows;
4167  }
4168 }
setTimeout($timeout)
Set Timeout.
Definition: SSH2.php:2342
$languages_server_to_client
Definition: SSH2.php:301
getBannerMessage()
Returns the banner message.
Definition: SSH2.php:3949
_generate_identifier()
Generates the SSH identifier.
Definition: SSH2.php:1114
getLastError()
Returns the last error.
Definition: SSH2.php:3788
_get_interactive_channel()
Return the channel to be used with read() / write()
Definition: SSH2.php:2619
$kex_dh_group_size_preferred
Definition: SSH2.php:220
enableQuietMode()
Enable Quiet Mode.
Definition: SSH2.php:3103
$keyboard_requests_responses
Definition: SSH2.php:758
$result
const LOG_MAX_SIZE
Make sure that the log never gets larger than this.
Definition: SSH2.php:146
_define_array()
Define Array.
Definition: SSH2.php:3648
Pure-PHP implementation of Twofish.
$timeout
Timeout.
Definition: SSH2.php:640
$type
$channel_extended_data_type_codes
Definition: SSH2.php:468
Pure-PHP implementation of Blowfish.
__destruct()
Destructor.
Definition: SSH2.php:2849
const PUBLIC_FORMAT_RAW
#-
Definition: RSA.php:212
$packet_size_client_to_server
Definition: SSH2.php:537
$h
_login($username)
Login Helper.
Definition: SSH2.php:1867
_format_log($message_log, $message_number_log)
Formats a log for printing.
Definition: SSH2.php:3696
stopSubsystem()
Stops a subsystem.
Definition: SSH2.php:2797
_close_channel($client_channel, $want_reply=false)
Closes and flushes a channel.
Definition: SSH2.php:3577
$engine
Definition: workflow.php:89
getCompressionAlgorithmsServer2Client()
Return a list of the compression algorithms the server supports, when sending stuff to the client...
Definition: SSH2.php:3907
$last_interactive_response
Definition: SSH2.php:749
$mac_algorithms_server_to_client
Definition: SSH2.php:274
setCryptoEngine($engine)
Set Crypto Engine Mode.
Definition: SSH2.php:983
const LOG_REALTIME_FILE
Dumps the content real-time to a file.
Definition: SSH2.php:128
_get_binary_packet()
Gets Binary Packets.
Definition: SSH2.php:2885
const CHANNEL_SHELL
Definition: SSH2.php:104
const MASK_CONSTRUCTOR
#+ Execution Bitmap Masks
Definition: SSH2.php:79
const MODE_CTR
#+ public
Definition: Base.php:62
$window_size_client_to_server
Definition: SSH2.php:589
const MASK_LOGIN
Definition: SSH2.php:82
getLog()
Returns a log of the packets that have been sent and received.
Definition: SSH2.php:3670
$s
Definition: pwgen.php:45
_on_channel_open()
Helper function for agent->_on_channel_open()
Definition: SSH2.php:3745
$index
Definition: metadata.php:60
$server_host_key_algorithms
Definition: SSH2.php:238
setWindowSize($columns=80, $rows=24)
Sets the number of columns and rows for the terminal window size.
Definition: SSH2.php:4163
$keys
_encryption_algorithm_to_key_size($algorithm)
Maps an encryption algorithm name to the number of key bytes.
Definition: SSH2.php:1763
setWindowColumns($value)
Sets the number of columns for the terminal window size.
Definition: SSH2.php:4140
getExitStatus()
Returns the exit status of an SSH command or false.
Definition: SSH2.php:4104
$w
const MASK_SHELL
Definition: SSH2.php:83
getMACAlgorithmsClient2Server()
Return a list of the MAC algorithms the server supports, when receiving stuff from the client...
Definition: SSH2.php:3868
const CHANNEL_EXEC
#-
Definition: SSH2.php:103
__construct($host, $port=22, $timeout=10)
Default Constructor.
Definition: SSH2.php:881
$channel_open_failure_reasons
Definition: SSH2.php:448
const MASK_WINDOW_ADJUST
Definition: SSH2.php:84
const CHANNEL_SUBSYSTEM
Definition: SSH2.php:105
isConnected()
Is the connection still active?
Definition: SSH2.php:2860
$start
Definition: bench.php:8
enablePTY()
Enable request-pty when using exec()
Definition: SSH2.php:3138
_array_intersect_first($array1, $array2)
Returns the first value of the intersection of two arrays or false if the intersection is empty...
Definition: SSH2.php:3761
getStdError()
Get the output from stdError.
Definition: SSH2.php:2352
write($cmd)
Inputs a command into an interactive shell.
Definition: SSH2.php:2706
_key_exchange($kexinit_payload_server)
Key Exchange.
Definition: SSH2.php:1148
read($expect='', $mode=self::READ_SIMPLE)
Returns the output of an interactive shell.
Definition: SSH2.php:2661
Pure-PHP implementation of SSHv2.
Pure-PHP implementation of Rijndael.
$window_size_server_to_client
Definition: SSH2.php:578
$r
Definition: example_031.php:79
catch(Exception $e) $message
disablePTY()
Disable request-pty when using exec()
Definition: SSH2.php:3148
getWindowRows()
Returns the number of rows for the terminal window size.
Definition: SSH2.php:4129
$y
Definition: example_007.php:83
isAuthenticated()
Have you successfully been logged in?
Definition: SSH2.php:2871
getErrors()
Returns all errors.
Definition: SSH2.php:3777
_privatekey_login($username, $privatekey)
Login with an RSA private key.
Definition: SSH2.php:2239
const MASK_LOGIN_REQ
Definition: SSH2.php:81
$realtime_log_wrap
Real-time log file wrap boolean.
Definition: SSH2.php:683
Pure-PHP PKCS#1 compliant implementation of RSA.
getEncryptionAlgorithmsClient2Server()
Return a list of the (symmetric key) encryption algorithms the server supports, when receiving stuff ...
Definition: SSH2.php:3842
_initShell()
Creates an interactive shell.
Definition: SSH2.php:2515
_disconnect($reason)
Disconnect.
Definition: SSH2.php:3610
$curTimeout
Current Timeout.
Definition: SSH2.php:648
const LOG_SIMPLE
#-
Definition: SSH2.php:116
$compression_algorithms_server_to_client
Definition: SSH2.php:292
getKexAlgorithms()
Return a list of the key exchange algorithms the server supports.
Definition: SSH2.php:3816
Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic...
$n
Definition: RandomTest.php:85
Pure-PHP implementation of Triple DES.
disconnect()
Disconnect.
Definition: SSH2.php:2833
getWindowColumns()
Returns the number of columns for the terminal window size.
Definition: SSH2.php:4118
getLanguagesClient2Server()
Return a list of the languages the server supports, when receiving stuff from the client...
Definition: SSH2.php:3933
const MASK_CONNECTED
Definition: SSH2.php:80
isPTYEnabled()
Returns whether request-pty is enabled or not.
Definition: SSH2.php:3161
getLanguagesServer2Client()
Return a list of the languages the server supports, when sending stuff to the client.
Definition: SSH2.php:3920
_filter($payload)
Filter Binary Packets.
Definition: SSH2.php:2978
getServerPublicHostKey()
Returns the server public host key.
Definition: SSH2.php:3963
_append_log($message_number, $message)
Logs data packets.
Definition: SSH2.php:3451
$filename
Definition: buildRTE.php:89
const READ_REGEX
Returns when a string matching the regular expression $expect is found.
Definition: SSH2.php:142
_format_log_helper($matches)
Helper function for _format_log.
Definition: SSH2.php:3731
_string_shift(&$string, $index=1)
String Shift.
Definition: SSH2.php:3631
$rows
Definition: xhr_table.php:10
_send_binary_packet($data, $logged=null)
Sends Binary Packets.
Definition: SSH2.php:3393
$compression_algorithms_client_to_server
Definition: SSH2.php:283
const SIGNATURE_PKCS1
Use the PKCS#1 scheme by default.
Definition: RSA.php:124
getServerHostKeyAlgorithms()
Return a list of the host key (public key) algorithms the server supports.
Definition: SSH2.php:3829
_ssh_agent_login($username, $agent)
Login with an ssh-agent provided key.
Definition: SSH2.php:2216
getCompressionAlgorithmsClient2Server()
Return a list of the compression algorithms the server supports, when receiving stuff from the client...
Definition: SSH2.php:3894
disableQuietMode()
Disable Quiet Mode.
Definition: SSH2.php:3115
isQuietModeEnabled()
Returns whether Quiet Mode is enabled or not.
Definition: SSH2.php:3128
$password
Definition: cron.php:14
login($username)
Login.
Definition: SSH2.php:1851
$encryption_algorithms_client_to_server
Definition: SSH2.php:247
Pure-PHP arbitrary precision integer arithmetic library.
Pure-PHP implementation of RC4.
_keyboard_interactive_login($username, $password)
Login via keyboard-interactive authentication.
Definition: SSH2.php:2070
_get_open_channel()
Return an available open channel.
Definition: SSH2.php:2637
$languages_client_to_server
Definition: SSH2.php:310
const READ_SIMPLE
#-
Definition: SSH2.php:138
_get_channel_packet($client_channel, $skip_extended=false)
Gets channel data.
Definition: SSH2.php:3175
_keyboard_interactive_process()
Handle the keyboard-interactive requests / responses.
Definition: SSH2.php:2101
$i
Definition: disco.tpl.php:19
getMACAlgorithmsServer2Client()
Return a list of the MAC algorithms the server supports, when sending stuff to the client...
Definition: SSH2.php:3881
$encryption_algorithms_server_to_client
Definition: SSH2.php:256
getServerIdentification()
Return the server identification.
Definition: SSH2.php:3803
static string($length)
Generate a random string.
Definition: Random.php:54
exec($command, $callback=null)
Execute Command.
Definition: SSH2.php:2368
_send_channel_packet($client_channel, $data)
Sends channel data.
Definition: SSH2.php:3529
getEncryptionAlgorithmsServer2Client()
Return a list of the (symmetric key) encryption algorithms the server supports, when sending stuff to...
Definition: SSH2.php:3855
isTimeout()
Is timeout?
Definition: SSH2.php:2823
$mac_algorithms_client_to_server
Definition: SSH2.php:265
$response
const LOG_REALTIME
Outputs the content real-time.
Definition: SSH2.php:124
setWindowRows($value)
Sets the number of rows for the terminal window size.
Definition: SSH2.php:4151
if(! $in) $columns
Definition: Utf8Test.php:45
_encryption_algorithm_to_crypt_instance($algorithm)
Maps an encryption algorithm name to an instance of a subclass of .
Definition: SSH2.php:1803
$key
Definition: croninfo.php:18
reset()
Closes a channel.
Definition: SSH2.php:2811
$x
Definition: complexTest.php:9
const CHANNEL_AGENT_FORWARD
Definition: SSH2.php:106
_login_helper($username, $password=null)
Login Helper.
Definition: SSH2.php:1898
const LOG_COMPLEX
Returns the message content.
Definition: SSH2.php:120
_connect()
Connect to an SSHv2 server.
Definition: SSH2.php:994
$quiet_mode
Flag to suppress stderr from output.
Definition: SSH2.php:691
startSubsystem($subsystem)
Start a subsystem.
Definition: SSH2.php:2735
$data
Definition: bench.php:6